martin
martin

Reputation: 1007

MSVC compiler instantiates a function template's default definition even though a specialization exists

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

Answers (1)

Alan Birtles
Alan Birtles

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

Related Questions