I have to use an external library, but am getting a "multiple definition error" from following template function and its explicit specialization, if it gets called with a std::string.
template <typename T>
void foo(T& value);
template <>
void foo(std::string& value);
even if I change the 2nd function to
void foo(std::string& value);
the problem is the same.
According to [1] at least the version without a template (the "plain old function") should be prefered over the template version.
Does anybody have a clue, where the problem could be?
[1] http://www.gotw.ca/publications/mill17.htm
You're breaking the one-definition rule.
Unless a function is inline, it can only be defined once. If you mark the function as inline, so long as the definitions match they can be defined as often as desired. Template functions behave as if they were implicitly inline, so you don't get errors with templates.
However, an explicit specialization or non-template function is not implicitly inline and because you're including it in multiple translation units, you get multiple definitions; this breaks the rule. You should mark it as inline:
template <>
inline void foo(std::string& value);
(If you're getting this before link time, you need include guards.)
Related
I am referring to this answer:
https://stackoverflow.com/a/4447057/930315
I ran into a similar issue as the OP of the cited question,
having a function
template<typename T>
void func(T& val);
and its specialization
template<>
void func<mytype>(mytype& val);
resulted in a duplicate symbols linker error (the methods are implemented in a '.tpp' file that is included at the end of my header).
adding inline to the specialised function resolved the issue. Why?
Well, if you want the standard quote on this, that'd be over at [temp.expl.spec]/12
An explicit specialization of a function or variable template is
inline only if it is declared with the inline specifier or defined as
deleted, and independently of whether its function or variable
template is inline. [ Example:
template<class T> void f(T) { /* ... */ }
template<class T> inline T g(T) { /* ... */ }
template<> inline void f<>(int) { /* ... */ } // OK: inline
template<> int g<>(int) { /* ... */ } // OK: not inline
— end example ]
That's why you have to do it. It's independent because I believe doing otherwise would be needlessly restrictive, as Yola demonstrated.
This will work without inline:
file1.h
template<typename T> void func(T& val);
template<> void func<mytype>(mytype& val);
file1.cpp
template<> void func<int>(int& ) {}
But if you define template specialization in header file, than you may violate ODR
According to clause 3.2:4 in the c++ standard
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required. The definition can appear explicitly in the program, it can
be found in the standard or a user-defined library, or (when
appropriate) it is implicitly defined (see 12.1, 12.4 and
12.8). An inline function shall be defined in every translation unit in which it is odr-used.
This explains why there is a link-time error when the specialized function is not declared inline. The program will contain multiple definitions of the specialized function, one from each module including the .tpp-file and this breaks the condition from the standard. When declaring the specialized function inline it will make the function satisfy the second part of the same clause, i.e. that an inline function must be defined in each module using the function.
When the parameterized function is not specialized it is covered by clause 3.2:6:
There can be more than one definition of a class type (Clause 9),
enumeration type (7.2), inline function with external linkage (7.1.2),
class template (Clause 14), non-static function template (14.5.6),
static data member of a class template (14.5.1.3), member function of
a class template (14.5.1.1), or template specialization for which some
template parameters are not specified (14.7, 14.5.5) in a program
provided that each definition appears in a different translation unit
This clause states that it is OK for multiple definitions of the same template function as long as at least one of the template parameters is not specified in the code. This is to allow the decision on whether the parameterized function should be instantiated in a module to be made on local information only.
Is it somehow possible to separate the definition and declaration of a class-method with template arguments (especially used when using constexpr functions) in distinct places? Because aren't "template arguments" like explicit specializations of template-functions?
Or is this situation tangled with the already well-discussed topics:
Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?
Why can templates only be implemented in the header file?
E.g.:
Header file "someHeader.h"
#include <iostream>
#pragma once
class cc
{
public:
cc()=default;
~cc()=default;
template<uint32 f_val2Check_u32>
constexpr uint32 isPowerOf2();
private:
};
Then the *.cpp file:
// cpp-file
#include "someHeader.h"
template<uint32 val>
constexpr uint32 cc::isPowerOf2()
{
return ((val&(val-1))==0);
}
The problem is that templates are instantiated at compile time.
So it needs to see the definition of the function or class to understand what all class of operations would be applied on the type being passed.
Lets say in your function declaration :
template <typename T> void print_square(T t)
By seeing this what can you tell about :
"What all operations are going to be applied on the type T" ?
Honestly, nothing.
Now lets have a look at function definition :
template <typename T>
void print_square(T t)
{
std::cout << t * t;
}
Now when we see the function definition, we can tell that a binary operator * should be applicable on the type T.
This is the requirement on the type being passed to template.
In better words, the class of operations applicable of the type T being passed.
Hence, the compiler needs access to the function definition of a function template.
It can stop you from passing std::string as an argument to print_square(), since it doesn't meet the requirement.
The compiler must know what's the implementation of the function in all the translation units where it is used. The constexpr keyword tells the compiler to compute the result of the function for the given parameters at compile time and propagate that value. Without the implementation this cannot be achieved.
This would work on non-template non-constexpr functions because the value can be computed at run time, i.e. after linking.
I am referring to this answer:
https://stackoverflow.com/a/4447057/930315
I ran into a similar issue as the OP of the cited question,
having a function
template<typename T>
void func(T& val);
and its specialization
template<>
void func<mytype>(mytype& val);
resulted in a duplicate symbols linker error (the methods are implemented in a '.tpp' file that is included at the end of my header).
adding inline to the specialised function resolved the issue. Why?
Well, if you want the standard quote on this, that'd be over at [temp.expl.spec]/12
An explicit specialization of a function or variable template is
inline only if it is declared with the inline specifier or defined as
deleted, and independently of whether its function or variable
template is inline. [ Example:
template<class T> void f(T) { /* ... */ }
template<class T> inline T g(T) { /* ... */ }
template<> inline void f<>(int) { /* ... */ } // OK: inline
template<> int g<>(int) { /* ... */ } // OK: not inline
— end example ]
That's why you have to do it. It's independent because I believe doing otherwise would be needlessly restrictive, as Yola demonstrated.
This will work without inline:
file1.h
template<typename T> void func(T& val);
template<> void func<mytype>(mytype& val);
file1.cpp
template<> void func<int>(int& ) {}
But if you define template specialization in header file, than you may violate ODR
According to clause 3.2:4 in the c++ standard
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required. The definition can appear explicitly in the program, it can
be found in the standard or a user-defined library, or (when
appropriate) it is implicitly defined (see 12.1, 12.4 and
12.8). An inline function shall be defined in every translation unit in which it is odr-used.
This explains why there is a link-time error when the specialized function is not declared inline. The program will contain multiple definitions of the specialized function, one from each module including the .tpp-file and this breaks the condition from the standard. When declaring the specialized function inline it will make the function satisfy the second part of the same clause, i.e. that an inline function must be defined in each module using the function.
When the parameterized function is not specialized it is covered by clause 3.2:6:
There can be more than one definition of a class type (Clause 9),
enumeration type (7.2), inline function with external linkage (7.1.2),
class template (Clause 14), non-static function template (14.5.6),
static data member of a class template (14.5.1.3), member function of
a class template (14.5.1.1), or template specialization for which some
template parameters are not specified (14.7, 14.5.5) in a program
provided that each definition appears in a different translation unit
This clause states that it is OK for multiple definitions of the same template function as long as at least one of the template parameters is not specified in the code. This is to allow the decision on whether the parameterized function should be instantiated in a module to be made on local information only.
Here's what I mean:
// test.h
class cls
{
public:
template< typename T >
void f( T t );
};
-
// test.cpp
template<>
void cls::f( const char* )
{
}
-
// main.cpp
int main()
{
cls c;
double x = .0;
c.f( x ); // gives EXPECTED undefined reference (linker error)
const char* asd = "ads";
c.f( asd ); // works as expected, NO errors
return 0;
}
This is completely fine, right?
I started doubting this, because I just ran over the specialization of '...' after instantiation error, which was new to me. So, I "worked around" this error and everything seems to work fine now, but still..
Is this well-defined behavior?
edit: And the same for non-member template functions (forward declared non-member template functions).
Lightness Races in Orbit cited why it's not compliant parts from the Standard. There might be some others, in the vicinity.
I will try to explain in simpler terms what the Standard verbiage means, and hopefully I'll get it correctly, and finally explain the linker errors (or absence of error):
What is the point of instantiation ?
How does the compiler select a specialization ?
What is necessary at the point of instantiation ?
Why a linker error ?
1/ What is the point of instantiation ?
The point of instantiation of a template function is the point where it is called or referred to (&std::sort<Iterator>) with all the template parameters fleshed out (*).
template <typename T>
void foo(T) { std::cout << typeid(T).name() << "\n"; }
int main() { foo(1); } // point of instantiation of "foo<int>(int)"
It can be delayed though, and thus not match the exact call site, for templates called from other templates:
template <typename T>
void foo(T) { std::cout << typeid(T).name() << "\n"; }
template <typename T>
void bar(T t) { foo(t); } // not a point of instantiation, T is still "abstract"
int main() { foo(1); } // point of instantiation of "bar<int>(int)"
// and ALSO of "foo<int>(int)"
This delay is very important as it enables writing:
co-recursive templates (ie, templates that refer to each-others)
user-specializations
(*) Roughly speaking, there are exceptions such as non-template methods of a template class...
2/ How does the compiler select a specialization ?
At the point of instantiation, a compiler need to be able to:
decide which base template function to call
and possibly, which of its specializations to call
This old GotW shows off the woes of specializations... but in short:
template <typename T> void foo(T); // 1
template <typename T> void foo(T*); // 2
are overloads, and each spawns a distinct family of possible specializations of which they are the base.
template <> void foo<int>(int);
is a specialization of 1, and
template <> void foo<int*>(int*);
is a specialization of 2.
In order to resolve the function call, the compiler will first pick the best overload, while ignoring template specializations, and then, if it picked a template function, check if it has any specialization that could better apply.
3/ What is necessary at the point of instantiation ?
So, from the way a compiler resolve the call, we understand why the Standard specifies that any specialization should be declared before its first point of instantiation. Otherwise, it simply would not be considered.
Thus, at the point of instantiation, one needs to have already seen:
a declaration of the base template function to be used
a declaration of the specialization to be selected, if any
But what of the definition ?
It is not needed. The compiler assumes it will either be provided later on in the TU or by another TU entirely.
Note: it does burden the compiler because it means it needs to remember all the implicit instantiations it encountered and for which it could not emit a function-body so that when it finally encounters the definition it can (at last) emit all the necessary code fo all the specializations it encountered. I wonder why this particular approach was selected, and also wonder why even in the absence of an extern declaration the TU may end with undefined function-bodies.
4/ Why a linker error ?
Since no definition is provided, gcc trusts you to provide it later and simply emits a call to an unresolved symbol. If you happen to link with another TU that provides this symbol, then everything will be fine, and otherwise you'll get a linker error.
Since gcc follows the Itanium ABI we can simply look up how it mangles the symbols. It turns out that the ABI makes no difference in mangling specializations and implicit instantiations thus
cls.f( asd );
calls _ZN3cls1fIPKcEEvT_ (which demangles as void cls::f<char const*>(char const*)) and the specialization:
template<>
void cls::f( const char* )
{
}
also produces _ZN3cls1fIPKcEEvT_.
Note: it is not clear to me whether an explicit specialization could have been given a different mangling.
No, I don't think it's okay:
[C++11: 14/6]: A function template, member function of a class template, or static data member of a class template shall be defined in every translation unit in which it is implicitly instantiated (14.7.1) unless the corresponding specialization is explicitly instantiated (14.7.2) in some translation unit; no diagnostic is required.
[C++11: 14.7.3/6]: 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. [..]
Frankly I can't explain why it works for you.
I think that your original code was incorrect and your "workaround" is not standard-compliant, too (despite the fact that your compiler and linker process it). Good quotes from the standard were cited in the answer of #Lightness Races in Orbit. See also the following example from the standard ([temp.expl.spec] 14.7.3/6):
class String { };
template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }
void f(Array<String>& v) {
sort(v); // use primary template
// sort(Array<T>&), T is String
}
template<> void sort<String>(Array<String>& v); // error: specialization
// after use of primary template
template<> void sort<>(Array<char*>& v); // OK: sort<char*> not yet used
I marked my answer as community wiki because in fact it is only a big comment.
The problem with template specializations is that they're treated like normal functions because there is no template parameter used anywhere anymore.
Therefor if the following code is put in a header file it works at first.
template <typename foo>
void f(foo p)
{
std::cout << "f one" << std::endl;
}
template <>
void f<int>(int p)
{
std::cout << "f two" << std::endl;
}
But this stops working if the header is included in two files.
In this case the error I get (with VS2010) is:
templateordering.obj : error LNK2005: "void __cdecl f<int>(int)" (??$f#H##YAXH#Z) already defined in othertu.obj
And this is fixable by using the inline keyword as mentioned in many other questions.
template <>
inline void f<int>(int p)
{
std::cout << "f two" << std::endl;
}
Now this raises two questions for me:
Is there any other way to do this? Putting the specialized function in the source file doesn't seem to work. Probably because I would need some sort of declaration in the header.
What does inline actually do? It seems to be a common rule of thumb all over the internet that inline shouldn't be used because the compiler "will probably inline the function the way he likes in any case". So if the compiler may not inline a function that I declare as "inline", why does this work?
Is there any other way to do this? Putting the specialized function in the source file doesn't seem to work. Probably because I would need some sort of declaration in the header.
You need to declare the specialisation in the header, just like any other function. It's up to you whether to define it inline in the header, or in (exactly one) source file; again, just like any other function. As you say, it must be declared inline if you do define it in the header.
What does inline actually do?
Normally, the one-definition rule requires functions to be defined in exactly one translation unit in the program. In practice this means that you can't define a function in a header, since a header is intended to be included in more than one translation unit.
However, sometimes you want, or need, to define functions in headers - for example, some compilers are only able to inline a function call if they can see the definition. The inline keyword relaxes the rule, so that you can define the function in multiple translation units, as long as all the definitions are identical.
inline enables some changes to the one definition rule. Specifically, a function (including an explicit specialization of a function template) declared inline can be defined in multiple translation units (provided the definitions in different translation units are the same) and must be defined in any translation unit where it is odr-used. This is the same version of the odr rule as applies to function templates (unspecialized).
You either have to define your specialization in one translation unit (and declare it before you use it in other translation units), or you can leave the definition in the header file but make it inline.
A declaration of your specialization would look like this:
template<> void f<int>(int p);
If you use the functions with the same type from two or more files, then the functions will be defined in each file. It's just the same as having a non-template function definition in a header file that is included in multiple source files.
Marking a function as inline hints to the compiler that it can "inline" the function, i.e. put the body of the function directly in place of where the function was called. This means that the function isn't actually defined. The compiler can also decide to not inline the function, in which case the inline keyword behaves like the static keyword.
Is there any other way to do this?
Putting the specialized function in the source file doesn't seem to work. Probably because I
would need some sort of declaration in the header.
Yes, if you separate the declaration and the definition of f<int>, that is supposed to solve the linker error.
The syntax of the declaration in .h file:
template <>
void f<int>(int p);
Is there any other way to do this?
template <>
static void f<int>(int p)
{
std::cout << "f two" << std::endl;
}
or have them inline, but use special compiler flags (where available) to suppress actual inlining
What does inline actually do?
Best answer(s) are already available :)