Template specialization, multiple defined symbols
Asked Answered
S

3

24

I know I'm missing something easy here but I've got a templated member function of a class which I've specialised.

class MyClass
{
    template<typename T> T GetTFromVariable(shared_ptr<TOtSimpleVariable> v, string s);
}

template<typename T>
T MyClass::GetTFromVariable(shared_ptr<TOtSimpleVariable> v, string s)
{
    throw std::runtime_error("Don't know how to convert " + ToString(v->GetString()));
}

template<>
int MyClass::GetTFromVariable<int>(shared_ptr<TOtSimpleVariable> v, string s)
{
    return v->GetInteger();
}

template<>
string MyClass::GetTFromVariable<string>(shared_ptr<TOtSimpleVariable> v, string s)
{
    return v->GetString();
}

// etc for other specialisations.

This is defined in my header file (as templates should be) but when I go to compile, I get a bunch of multiple defined symbols errors, such as:

OtCustomZenith_logic.lib(PtPathOutput.obj) : error LNK2005: "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall MyClass::GetTFromVariable<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(class boost::shared_ptr<class TOtSimpleVariable>,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??$GetTFromVariable@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@CommandProperties@@QAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$shared_ptr@VTOtSimpleVariable@@@boost@@V12@@Z) already defined in TableFareSystem_test.obj

I can fix it by inlining the method but I don't think that should be necessary... what am I missing?

EDIT: I'm using Visual Studio 2010

Sapphire answered 15/12, 2010 at 5:27 Comment(1)
What toolchain are you using? GCC? Visual something? If GCC, what do the build and link lines look like?Tinner
F
31

As Alf noted, the full specialization is no longer a template. However, I am not sure it has to be defined inline. You should also be able to split the declaration and definition.

I.e. In your header have:

template<> 
int MyClass::GetTFromVariable<int>(shared_ptr<TOtSimpleVariable> v, string s);

and in the implementation have:

template<>
int MyClass::GetTFromVariable<int>(shared_ptr<TOtSimpleVariable> v, string s)
{
    return v->GetInteger();
}

I also had thought that by rights the templated definition should also be explicitly inline (I always have done so) but would not be too surprised if a given compiler was lax in applying the ODR for templates. I'd be interested to see the standard reference that states otherwise.

Forsake answered 15/12, 2010 at 6:45 Comment(2)
I'm sorry about the imprecise wording. I was only talking about the OP's code as it were/is presented, with the definition in the header file. Thanks for clarifying! Cheers,Pisolite
moving the specialization from the header to a single .cpp file implies the need to compile and link to an .obj or .lib harming usability. better to keep it inline as suggested by @alf. I'm down voting your answer because of this.Bohemian
P
38

The full specialization is no longer a template. It's a concrete function. As such it needs to be (implicitly or explicitly) declared inline. The easiest is to add that keyword before the return type specification.

That said, the presented code does not correspond to your error message.

Your error message talks about return type std::string, not return type int.

Pisolite answered 15/12, 2010 at 5:44 Comment(3)
"Your error message talks about return type std::string, not return type int." ... which means it's using the first (general) template, which returns type T (in this case, std::string).Tinner
@Mike: no, it means the code presented does not match the error message. i could guess that the OP has several explicit specialiations, not just one, and that he selected an arbitrary error message. but it would just be a guess. i'm saying, when u seek help, try to be precise, don't introduce irrelevant issues.Pisolite
Thanks @alf, you were correct and I've updated the question to more accurately reflect the problem.Sapphire
F
31

As Alf noted, the full specialization is no longer a template. However, I am not sure it has to be defined inline. You should also be able to split the declaration and definition.

I.e. In your header have:

template<> 
int MyClass::GetTFromVariable<int>(shared_ptr<TOtSimpleVariable> v, string s);

and in the implementation have:

template<>
int MyClass::GetTFromVariable<int>(shared_ptr<TOtSimpleVariable> v, string s)
{
    return v->GetInteger();
}

I also had thought that by rights the templated definition should also be explicitly inline (I always have done so) but would not be too surprised if a given compiler was lax in applying the ODR for templates. I'd be interested to see the standard reference that states otherwise.

Forsake answered 15/12, 2010 at 6:45 Comment(2)
I'm sorry about the imprecise wording. I was only talking about the OP's code as it were/is presented, with the definition in the header file. Thanks for clarifying! Cheers,Pisolite
moving the specialization from the header to a single .cpp file implies the need to compile and link to an .obj or .lib harming usability. better to keep it inline as suggested by @alf. I'm down voting your answer because of this.Bohemian
F
3

I would suggest you to remove the following implementation from your code altogether, so that compiler can generate error at compile-time itself if T is not int . Early detection of error is better than delay detection(which is done at runtime).

template<typename T>
T MyClass::GetTFromVariable(shared_ptr<TOtSimpleVariable> v, string s)
{
    throw std::runtime_error("Don't know how to convert " + ToString(v->GetString()));
}

There is exactly similar topic/issue discussing this thing. Please have a look at this:

Partial template specialization for specific type, c++

Fonseca answered 15/12, 2010 at 5:54 Comment(1)
better that removing it is leaving this but use static_assert(false, "explain the problem and the solution"). this will help developers understand what is wrong if the error results.Bohemian

© 2022 - 2024 — McMap. All rights reserved.