I write a class with template method and use it like below.
#include<string>
using namespace std;
class A {
public:
template<typename T> void DoSomething(uint32_t index, T arg);
};
template<>
void A::DoSomething(uint32_t index,const string &arg) {
}
void Run(A &a, const string &str) {
a.DoSomething(0, str);
a.DoSomething<const string &>(0, str);
}
int main() {
A a;
Run(a, "HelloWorld");
return 0;
}
This code failed with linker error : undefined reference to `void A::DoSomething<std::__cxx11::basic_string<char, std::char_traits, std::allocator > >(unsigned int, std::__cxx11::basic_string<char, std::char_traits, std::allocator >)
So compiler discards the const reference qualifiers.
Why this happens ? How can fix it ?
CodePudding user response:
The problem is that the second parameter arg
in DoSomething
expects an argument to be passed by value. This means that T
is deduced to be std::string
and hence the general version of the function template is selected. But since there is no definition for the general version, you get the mentioned linker error.
For example,
template<typename T>
//--------vvvvv----------->by value
void func(T arg)
{
}
int main()
{
const std::string str = "name";
func(str); //T is std::string
}
CodePudding user response:
It's seems the correct answer to this question is perfect forwarding mixed with type traits.
below code shows how PerfectForwardDoSomething method call correct method.
#include<string>
#include <vector>
#include <type_traits>
using namespace std;
class DataClass {
public:
string content;
bool isPersist;
DataClass(string content, bool isPersist) : content(std::move(content)), isPersist(isPersist) {}
};
class A {
private:
vector<DataClass> keepUntilDestroy;
public:
template<typename T>
void DoSomething(uint32_t index, T arg);
template<typename T>
void PerfectForwardDoSomething(uint32_t index, T &&arg) {
if constexpr (is_reference_v<T>) {
DoSomething<const typename decay<T>::type &>(index, std::forward<T>(arg));
} else {
DoSomething<T>(index, std::forward<T>(arg));
}
}
};
template<>
void A::DoSomething(uint32_t index, const DataClass &arg) {
}
template<>
void A::DoSomething(uint32_t index, DataClass arg) {
keepUntilDestroy.push_back(arg);
}
void RunRef(A &a, const DataClass &data) {
a.PerfectForwardDoSomething(0, data);
}
void RunVal(A &a, DataClass data) {
a.PerfectForwardDoSomething(0, data);
}
int main() {
A a;
RunVal(a, {"HelloWorld", false});
RunRef(a, {"HelloWorld2", true});
return 0;
}