I have done explicit specializations before, I just can't figure out why this does not work:
StringUtils.hpp
#ifndef AYC_STRINGUTILS_HPP
#define AYC_STRINGUTILS_HPP
#include <string>
class StringUtils {
public:
template <typename T>
static std::string toString(const T& t);
};
#include "StringUtils.tpp"
#endif //AYC_STRINGUTILS_HPP
StringUtils.tpp
#include "StringUtils.hpp"
template<typename T>
std::string StringUtils::toString(const T& t) {
return std::to_string(t);
}
template<>
std::string StringUtils::toString<std::string>(const std::string& t) {
return t;
}
The errors I get are linker errors complaining about multiple definitions of the function toString.
Many files in the project use #include "StringUtils.hpp".
How can I try to fix this error? Is something wrong in the class StringUtils?
In addition to the solution provided in the answer by Brian, you can declare the specialization in the .hpp/.tpp file and define it in a .cpp file.
StringUtils.hpp file:
#ifndef AYC_STRINGUTILS_HPP
#define AYC_STRINGUTILS_HPP
#include <string>
class StringUtils {
public:
template <typename T>
static std::string toString(const T& t);
};
// Generic implementation.
template<typename T>
std::string StringUtils::toString(const T& t) {
return std::to_string(t);
}
// Declation of the specialization.
template<>
std::string StringUtils::toString<std::string>(const std::string& t);
#endif //AYC_STRINGUTILS_HPP
StringUtils.cpp file:
#include "StringUtils.hpp"
// Definition of the specialization.
template<>
std::string StringUtils::toString<std::string>(const std::string& t) {
return t;
}
Test program:
#include <iostream>
#include "StringUtils.hpp"
int main()
{
std::string a("test");
std::cout << StringUtils::toString(a) << std::endl;
std::cout << StringUtils::toString(10) << std::endl;
}
Output of the test program:
test
10
An explicit (full) specialization of a function template is subject to the one-definition rule, so StringUtils::toString<std::string> must not be defined in multiple translation units. You can solve this problem by declaring it inline.
Template function specialization is almost always the wrong answer.
Classes are poor namespaces.
Simply overload instead of specialize.
namespace StringUtils {
template <typename T>
std::string toString(const T& t){
using std::to_string;
return to_string(t);
}
inline std::string toString(std::string s){ return std::move(s); }
}
overload resolution does what you want, and it permits efficient signature variation (like above, where I take s by-value, which could avoid an extra heap allocation).
Also note I enabled ADL extension of to_string for custom classes. Simply overload to_steing(X) in X's namespace and StringUtils::toString(X) finds it.
Your immediate problem is you need to mark the specialization inline.
Related
struct S
{
S(int);
S(std::string);
void foo(int);
void foo(std::string)
};
So my problem is that foo() should be invokable only with the type the ctor was. Solutions I can think of and problems with them:
Template the whole class. However this brings all the implementation details to the header, polluting it with many library #includes, making the API hard to read, making error messages long.
bool is_int runtime check. The assert() could be forgotten when implementing this API. The constraint is not evident from the API.
Separate classes. Violates DRY or forces all implementation to be extracted in .cpp-local functions - which is not so readable nor intuitive.
What other solutions are there? Or which of mine is the least silly?
Morally, your class is a template, but will only be instantiated with a type from a finite, known set of types. Your question is how to avoid some disadvantages of templates you identified: implementation details leaked, larger translation units, unintelligible API, and worse error messages.
The most straightforward way to implement this is to define and instantiate the templates out-of-line:
// S.h
#pragma once
#include <string>
#include <type_traits>
template<typename T>
struct S
{
static_assert(
std::is_same_v<T, int> ||
std::is_same_v<T, std::string>,
"S<T> only supports T=int or T=std::string"
);
S(T);
void foo(T);
};
extern template struct S<int>;
extern template struct S<std::string>
// S.cpp
#include "S.h"
#include <iostream>
template<typename T>
S<T>::S(T) { }
template<typename T>
void S<T>::foo(T t)
{
std::cout << "foo(" << t << ")\n";
}
template struct S<int>;
template struct S<std::string>;
// main.cpp
#include "S.h"
int main()
{
auto s = S{1};
s.foo(2); // prints "foo(2)\n"
}
Any translation units using S do not instantiate, and cannot instantiate, specializations of S. They rely on the instantiations provided. Implementation details are not exposed. Translation units are only as large as necessary to define the interface, not to implement it. The static assertion isn't required, but facilitates an early and obvious error.
As I got you have already implemented S. You can use the pimpl or S as a base class. Rename S to SImpl and make another templated S.
simpl.h
struct SImpl
{
SImpl(int);
SImpl(std::string);
void foo(int);
void foo(std::string)
};
s.h
#include "simpl.h"
template <typename T>
struct S: protected SImpl
{
S(T t) : SImpl(t) {}
void foo(T t) { SImpl::foo(t); }
};
I have a templated function defined as:
template<typename TObject> TObject Deserialize(long version, const Value &value)
what I need to do, is to write a specialization which would take vector defined as:
template<typename TNum, int cnt> class Vec
and still has access to cnt and TNum.
I have unsuccesfully tried
template<typename TNum, int cnt> Vec<TNum, cnt> Deserialize<Vec<TNum, cnt>>(long version, Value &value)
resulting in error: illegal use of explicit template arguments
What is the correct way to do it?
Usually, the correct answer to dealing with function templates and needing to partially specialize them, is to simply overload them instead. In this case this trick doesn't work directly because there are no arguments that depend on the template parameter, i.e. the template parameter is explicitly specified and not deduced. However, you can forward along to implementation functions, and make overloading work by using a simple tag struct.
#include <functional>
#include <iostream>
#include <type_traits>
#include <vector>
#include <array>
template <class T>
struct tag{};
template<typename TObject>
TObject Deserialize_impl(long version, tag<TObject>) {
std::cerr << "generic\n";
return {};
}
template<typename T, std::size_t N>
std::array<T,N> Deserialize_impl(long version, tag<std::array<T,N>>) {
std::cerr << "special\n";
return {};
}
template<typename TObject>
TObject Deserialize(long version) {
return Deserialize_impl(version, tag<TObject>{});
}
int main() {
Deserialize<int>(0);
Deserialize<std::array<int,3>>(0);
return 0;
}
Live example: http://coliru.stacked-crooked.com/a/9c4fa84d2686997a
I generally find these approaches strongly preferable to partial specialization of a struct with a static method (the other major approach here) as there are many things you can take advantage with functions, and it behaves more intuitively compared to specialization. YMMV.
While the functional tag-dispatch is a nice approach, here's a class specialization version for comparison. Both have their use, and I don't think either is an inherently regrettable decision but maybe one matches your personal style more.
For any class you write that needs a custom deserialize handler, just write a specialization of the Deserializer class:
#include <iostream>
#include <string>
using namespace std;
using Value = std::string;
// default deserialize function
template <typename TObject>
struct Deserializer {
static TObject deserialize(long version, const Value &value) {
std::cout << "default impl\n";
return TObject();
}
};
// free standing function (if you want it) to forward into the classes
template <typename TObject>
TObject deserialize(long version, const Value &value) {
return Deserializer<TObject>::deserialize(version, value);
}
// Stub example for your Vec class
template<typename TNum, int cnt> class Vec { };
// Stub example for your Vec deserializer specialization
template <typename TNum, int cnt> struct Deserializer<Vec<TNum, cnt>> {
static auto deserialize(long version, const Value &value) {
std::cout << "specialization impl: cnt=" << cnt << "\n";
return Vec<TNum, cnt>();
}
};
int main() {
Value value{"abcdefg"};
long version = 1;
deserialize<int>(version, value);
deserialize<Vec<int, 10>>(version, value);
}
Ideally in this situation, Vec should reflect its own template parameters as members Vec::value_type and Vec::size() which should be constexpr.
If the class fails to provide its own properties in its own interface, the next best thing is to define your own extension interface. In this situation, you can have separate metafunctions (like accessor functions), or a traits class (like a helper view class). I'd prefer the latter:
template< typename >
struct vector_traits;
template< typename TNum, int cnt >
struct vector_traits< Vec< TNum, cnt > > {
typedef TNum value_type;
constexpr static int size = cnt;
};
template<typename TVec> TVec Deserialize(long version, Value &value) {
typedef vector_traits< TVec > traits;
typedef typename traits::value_type TNum;
constexpr static int cnt = traits::size;
…
}
This solution fits into any existing function, and even makes the signatures cleaner. Also, the function is more flexible because you can adapt it by adding traits specializations instead of entire new overloads.
In one of my projects I have a tree data structure, that might contain values of a generic type T.
In order to reduce compilation times and dependencies I tried to move implementation details from the tree node class Node to class NodeImpl. The internals of Node are private and should be only accessed at defined entry points, for example by the getName function.
Only people that are interested in using getName should include NodeImpl in their source files. This is how I think to reduce my compilation times and dependencies.
But for some reason the following three toys classes will not compile. It says no access to private members. What I'm doing wrong?
File main.cpp:
#include <iostream>
#include "Node.h"
#include "NodeImpl.h"
int main(int argc, char** args) {
Node<int> n("Test", 2);
std::cout << getName(n) << std::endl;
}
File Node.h:
#pragma once
#include <string>
template<typename T>
class NodeImpl;
template<typename T>
class Node {
public:
typedef T value_type;
Node(const std::string& name, const T& value) : name(name), value(value) {}
private:
std::string name;
T value;
friend class NodeImpl<T>;
};
File NodeImpl.h:
#pragma once
#include "Node.h"
template<typename T>
std::string getName(Node<T>& n);
template<typename T>
class NodeImpl {
NodeImpl(Node<T>& node) : mNode(node) {
}
Node<T>& mNode;
std::string name() {
return mNode.name;
}
friend std::string getName(Node<T>& n);
};
template<typename T>
std::string getName(Node<T>& n) {
auto x = NodeImpl<T>(n);
return x.name();
}
The warning produced by GCC gives insight:
warning: friend declaration 'std::__cxx11::string getName(Node<T>&)' declares a non-template function [-Wnon-template-friend]
friend std::string getName(Node<T>& n);
^
note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
In other words, your class NodeImpl<int> was befriending a global function std::string getName(Node<int> &), which is unrelated to the function std::string getName<int>(Node<int> &) instantiated from the function template template <class T> std::string getName(Node<T> &) for T = int.
So the correct solution is this:
template<typename T>
class NodeImpl {
// ... as before ...
friend std::string getName<T>(Node<T>& n);
};
This [live example] shows that this solution indeed works.
I have working code like this.
#include <iostream>
struct A{
template<typename T>
void foo(T val);
};
template<typename T> void A::foo(T val)
{
std::cout << val << std::endl;
}
// link template "against" int
template void A::foo(int val);
// #include header here
int main(){
A a;
a.foo(12);
}
Template is in separate CPP file, but linking works, because of explicit instantiation:
template void A::foo(int val);
Then I did some re-factoring, and code looks like this:
#include <iostream>
template<typename G>
struct A{
template<typename T>
void foo(T val);
};
template<typename G>
template<typename T> void A<G>::foo(T val)
{
std::cout << val << std::endl;
}
// link template "against" int - not working
//template<typename G>
//template void A<G>::foo(int val);
int main(){
A<float> a;
a.foo(12);
}
How can I "link" T=int, but keep G "unknown"?
It is called explicit instantiation.
You can't do this, because G is unknown and it is not a single type. It is rather a set of types.
You can not do this. To actually produce a code from a template (I guess that's what you call link), the compiler need to know all the template parameters.
So you are left with the standard options for template instantiation: either explicitly tell the compiler what T and G will be used, either let the compiler see full code for the template member wherever you use it (that is, include the code in header).
TL;DR you can't.
In your case I'd just specify the type you intend to use
template void A<float>::foo(int val);
or (rather bulky) explicitly instantiate all the types G could be used for.
There is no way you can explicitly instantiate the template if G cannot be deduced.
Notice that linking works not because this syntax is a linker command but because your compiler is producing code that is later found at link-time. See more here
This question builds up on my:
C++ library: .hpp + .inl (separate definitions and declarations) vs .hpp only (in-class body code)
Moving templated functions into inline files is definitely an official hell. See below three examples. From my OLD neat way of doing things (#0), to a really expanded way (#1), to a more compacted way (#2) to the imaginary dream pipe way (#3).
// #0 the actual class definition
namespace CA {
template <typename tnChar>
class MyClass {
public:
typedef tnChar tChar;
typedef std::basic_string<tChar> tString;
MyClass(); // {}
tString Method1(const tString& aParam) const; // { return aParam; }
};
};
This is the inline version #1. It has both namespace and class.
template <typename tnChar>
CA::MyClass<tnChar>::MyClass(){}
template <typename tnChar>
typename CA::MyClass<tnChar>::tString
CA::MyClass<tnChar>::Method1(const tString& aParam) const {
return aParam;
}
template <> // specializing
typename CA::MyClass<wchar_t>::tString
CA::MyClass<wchar_t>::Method1(const tString& aParam) const {
return aParam;
}
This is the inline version #2. It is wrapped in namespace. Saved from adding the CA::.
namespace CA {
template <typename tnChar>
MyClass<tnChar>::MyClass(){}
template <typename tnChar>
typename MyClass<tnChar>::tString
MyClass<tnChar>::Method1(const tString& aParam) const {
return aParam;
}
template <> // specializing
typename MyClass<wchar_t>::tString
MyClass<wchar_t>::Method1(const tString& aParam) const {
return aParam;
}
}
This is the inline version #3. It is wrapped in namespace and class. Saved from adding the template <...> ... CA::MyClass. IMAGINARY version. Not possible but desirable!
#if 0 // like this is ever gonna work
// inline version #3 (IMAGINARY)
// wrapped in namespace and in class
// 'inline class' should make this functionality happen in C++my
namespace CA {
template <typename tnChar>
inline class MyClass { // :)
MyClass(){}
tString Method1(const tString& aParam) const {
return aParam;
}
};
template <>
inline class MyClass<wchar_t> { // :) - too much imagination
tString Method1(const tString& aParam) const {
return aParam;
}
};
}
#endif // disabled
My question is: Is anything like version 3 even remotely possible? Without having to typename the return values and write the template <...> each and every single time. I know the reasons why we need to do this, but I'm wondering if the new C++11 or the next C++14 has any plans to address template class inline method grouping?
Having the inline class keywords trigger the imaginary functionality in #3 would be mind blowing and it would make externalizing template class methods so easy... and logic... and grouped... and short :)
At the risk of stating the obvious, that syntax is available when declaring member functions inside the class body. If your religion requires you to put the member functions in a separate .inl file, you could include that file inside the class body itself.