Explicit specialization of member function template in source file
Asked Answered
D

1

12

I have a class with a member template function:

// writer.h
class Writer {
public:
    ...
    template <typename T, typename V>
    void addField(const std::string& name, V v) 
    {
        // write something
    }
};

And in Writer's source file, I added explicit specializations for some_type:

// writer.cpp
template <>
void Writer::addField<some_type, int>(const std::string& name, int v)
{
    // specific some_type writing logic
}

This works... sometimes. Even if I definitely make sure that I have the right types:

writer.addField<some_type>("name", static_cast<int>(some_value));

Sometimes the explicit specialization gets called, and sometimes the primary gets called. What gives?

Delibes answered 29/5, 2015 at 13:2 Comment(1)
Under Visual Studio its a compiler bug: Explicit specialization of C++ struct member template functions.Thermomotor
D
21

Declaring specializations in a source file and can cause all sorts of subtle issues that are very difficult to diagnose. The compiler isn't obligated to help you in any regard here either. The standard strongly encourages you not to do this, with the help of a limerick, in [temp.expl.spec]/6-7:

If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined.

The placement of explicit specialization declarations for function templates, class templates, variable templates, member functions of class templates, [...], etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

It's likely that in some translation units, the specialization happened to be declared before the first use - and in some translation units it hasn't been. It's better to avoid all such issues entirely by simply declaring your specialization in your header:

// writer.h
class Writer {
public:
    ...
    template <typename T, typename V>
    void addField(const std::string& name, V v) 
    { /* ... */ }
};

// still writer.h
template <>
inline void Writer::addField<some_type, int>(const std::string& name, int v)
{ /* ... */ }

You can also just declare it in the header (no longer needs to be inline), and still define it in the source.

Delibes answered 29/5, 2015 at 13:3 Comment(2)
If it's in the header, you need to make it inline; alternatively, you can declare the specialization in the header, and define it elsewhere.Testaceous
@Testaceous Ah, oops. Total omission on my part.Delibes

© 2022 - 2024 — McMap. All rights reserved.