Reputation: 1007
The following code compiles and behaves correctly on macOS, using Clang, but doesn't on Windows using MSVC 2017.
// File: toString.h
#include <string>
template<typename T>
const std::string toString(T){return "";}
// File: toString.cpp
#include "toString.h"
template <>
const std::string toString<int>(int value){
return std::to_string(value);
}
// File: main.cpp
#include <iostream>
#include "toString.h"
int main() {
// specialized
std::cout <<"int: "<< toString(1) << std::endl;
// not specialized
std::cout <<"double: "<< toString(1.0) << std::endl;
return 0;
}
// Expected output:
// int: 1
// double:
It fails at the linker, as the function is implicitly instantiated rather than linked to the int specialization, resulting in duplicate symbols.
If the default implementation of the template is removed, then the line printing the double
would fail as there would be no symbol to link it to.
My question is whether there is any way to achieve the same result on Windows with MSVC, without main.cpp having any visibility of the toString specialization (declaration or definition).
If not, is this covered by the standard or simply a compiler implementation detail?
Upvotes: 0
Views: 53
Reputation: 36379
There is nothing in tostring.h
to tell the compiler that your specialisation exists. Therefore when compiling main.cpp
the compiler simply instantiates the template declared in the header. The violates the one definition rule so the behaviour is undefined. That it works as you expect in clang is mostly due to luck in that of the two available definitions at link time clang chose the one you wanted.
To fix it you need to forward declare your specialisation in the header so that the compiler knows not to instantiate the template when compiling main.cpp
:
//toString.h
#include <string>
template<typename T>
const std::string toString( T ) { return ""; }
template <>
const std::string toString<int>( int value );
Upvotes: 1