I have a template class in C++ (somewhat simplified):
template<typename T>
struct C
{
T member;
void set(const &T x) { member = x; }
void set(int x) { member = x; }
};
As you can see the set() function can be called either with the type T, or with an int. This works fine unless T is an int, in which case I get an ambiguous conversion error. I understand why this is happening, but is there any way to implement what I want?
Provide a specialisation for int:
template<>
struct C<int>
{
int member;
void set(int x) { member = x };
};
?
One way around this would be to provide a specialisation of the template for int that only has one set function. Otherwise you might want to have a look at the Boost libraries if something like enable_if in their template meta programming code would allow you to turn on the function set(int x)only when T is not of type int.
could you cast the call to either int or const int in the calling code?
It depends what you're really trying to do. Your example as written doesn't make much sense as you're trying to set member, which is a T, from x, which is an int. While there are cases that may make sense I suspect in your real code you're setting something else.
Does the method have to have the same name? If so why?
Does specialising the struct for int make sense?
What other constraints do you have?
Perhaps this would work for you:
template<typename T>
struct C
{
T member;
template<typename U>
void set(const U& x) { member = x; }
void set(int x) { member = x; }
};
Now set(int) overloads set(const U&). set(const U&) accepts non T parameters, but will probably fail when you try to assign to X. It may allow more conversions than set( const T&).
If that's not good enough, adding an extra level of indirection should do the trick:
template<typename T>
struct C
{
T member;
void set(const T& x) { setInternal( x ); }
private:
template<typename U>
void setInternal(const U& x) { member = x; }
void setInternal(int x) { member = x; }
};
It appears that you want to make the set method available only if the argument is T or int. However, if you just specialize C for int, all the implicit conversions still happen, just as if you didn't attempt to treat int type in a special way at all.
If you really want to disable implicit conversions, one way is with boost::enable_if - make the method only available, if the criteria are satisfied.
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>
template<typename T>
struct C
{
T member;
template <class U>
typename boost::enable_if_c<
boost::is_same<T, U>::value || boost::is_same<U, int>::value
>::type
set(const U& x) { member = x; }
};
int main()
{
C<int> i;
i.set(3.14); //error
i.set(10); //OK
i.set('a'); //error
C<double> d;
d.set(3.14); //OK
d.set(3.14f); //error
d.set(10); //OK
d.set('a'); //error
}
To achieve the same result without boost::enable_if (or implementing it yourself), you might also need to make all unwanted versions of set private (C++0x also allows to delete these overloads).
template<typename T>
struct C
{
T member;
void set(const T& x) { member = x; }
void set(int x) { member = x; }
private:
template <class U>
void set(const U&);
};
template <>
struct C<int>
{
int member;
void set(int x) { member = x; }
private:
template <class U>
void set(const U&);
};
int main()
{
C<int> i;
i.set(3.14); //error
i.set(10); //OK
i.set('a'); //error
C<double> d;
d.set(3.14); //OK
d.set(3.14f); //error
d.set(10); //OK
d.set('a'); //error
}
However, making this exception for int seems rather arbitrary to me. Conversions from int might not be particularly safe, either, e.g int->float might lose precision for large values, int->short / int->char / int->unsigned might overflow.
Related
Let's say I have the following class:
template <typename T>
class SomeClass : Parent<T>
{
public:
// I have a function such as this one:
T DoSomething(const T &t)
{
return t.DoSomething(some_data);
}
// But `T` might be a pointer, so sometimes I will need something like the following
// instead (which obviously doesn't work as given):
T DoSomething(const T &t)
{
return new T(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
I got stuck in a giant mess of template errors trying to implement this in any semi-nice way possible using template specialization.
In the end I came up with this very ugly solution:
template <typename T>
class SomeClass : Parent<T>
{
public:
T DoSomething(const T &x)
{
return Specializer<T>::Do(this, x);
}
private:
template <typename V>
struct Specializer {
static V Do(SomeClass *me, const V &x)
{
return x.DoSomething(me->some_data);
}
};
template <typename V>
struct Specializer<V*> {
static V* Do(SomeClass *me, const V *&x)
{
return new V(x->DoSomething(me->some_data));
}
};
XYZ some_data;
};
Is there a better way to do this that doesn't involve stuffing this function into a dummy class/struct and passing around my this pointer?
PS: In reality, this has nothing to do with pointers, but rather with different types of containers. Pointers were just an easy example to use here.
You can avoid writing any specializations, and use a type trait like std::is_pointer along with if constexpr to decide what code to execute depending on the whether the type is a pointer type or not:
auto DoSomething(const T &t)
{
if constexpr (std::is_pointer_v<T>)
return new T(t->DoSomething(some_data));
else
return t.DoSomething(some_data);
}
If you don't want to check for whether T is a pointer, but want to check something else, you can still use this pattern by dropping in a suitable replacement for is_pointer.
If you have access to c++20, you can clean up the need for any SFINAE, specializations, or if constexpr by using concepts and constraints instead. This just allows you to define the same function N times with different criteria for its insantiation, which is much more readable IMO.
This is almost the same as the SFINAE approach, but without the need for the awful syntax (no std::declval, decltype, etc). It also doesn't require all implementations to exist in one function definition like the if constexpr approach; all you need is separate function definitions with different requires clauses:
#include <concepts>
...
template <typename T>
class SomeClass : Parent<T>
{
public:
// Work for everything that's not specialized
void DoSomething(const T &t)
{
std::cout << "Basic overload" << std::endl;
}
// Only work for pointers
void DoSomething(const T& t) requires(std::is_pointer_v<T>)
{
std::cout << "Pointer overload" << std::endl;
}
// Only work if T is convertible to SomeType
void DoSomething(const T& t) requires(std::convertible_to<T, SomeType>)
{
std::cout << "Convertible to SomeType overload" << std::endl;
}
private:
XYZ some_data;
};
Live Example
In this approach there are 3 different entries:
The basic fallback for all templates
An implementation that works for any pointer type, and
An implementation that works for any T type that may be convertible to SomeType
What about using SFINAE?
For example
#include <utility>
#include <iostream>
template <typename>
struct Parent
{ };
using XYZ = int;
template <typename T>
class SomeClass : Parent<T>
{
public:
template <typename U = T>
auto DoSomething (T const & t)
-> decltype( std::declval<U>().DoSomething(std::declval<XYZ>()) )
{ std::cout << "ref\n"; return t.DoSomething(some_data); }
template <typename U = T>
auto DoSomething (T const & t)
-> std::remove_reference_t<
decltype( std::declval<U>()->DoSomething(std::declval<XYZ>()),
std::declval<T>() )>
{
using V = std::remove_reference_t<decltype(*t)>;
std::cout << "pnt\n"; return new V(t->DoSomething(some_data));
}
private:
XYZ some_data;
};
struct foo
{
foo (foo*) {}
foo () {}
foo DoSomething (int) const { return {}; }
} ;
int main()
{
SomeClass<foo> sc1;
SomeClass<foo*> sc2;
foo f;
sc1.DoSomething(f);
sc2.DoSomething(&f);
}
I mean: what about enabling the first version if, and only if, T is a type that supports a DoSomething(XYZ) method and enabling the second version if, and only if, T is a pointer of a type that supports a DoSomething(XYZ) method?
I consider the following 2 snippets which do the same thing. In the first case, I don't use a universal reference, and so I have to write the function set twice. In the second case, with a universal reference I can write the function once, but the user of the class has to specify explicitly the type passed to the function set. Is there a trick to get both advantages?
#include <iostream>
struct A {};
template<typename T>
struct B {
void set(const A& a) {
a_=a;
}
void set(A&& a) {
a_=std::forward<A>(a);
}
A a_;
};
int main() {
B<A> b;
A a;
b.set({});
b.set(a);
return 0;
}
and
#include <iostream>
struct A {};
template<typename T>
struct B {
template<typename U>
void set(U&& a) {
a_=std::forward<U>(a);
}
A a_;
};
int main() {
B<A> b;
A a;
b.set(A{}); // here I can't do b.set({}) because the template parameter U would be undefined
b.set(a);
return 0;
}
In this case, you can add a default value to the U template parameter like
template<typename U = T>
void set(U&& a) {
a_=std::forward<U>(a);
}
and now if it can deduce the type, like with {} which has no type, then it will fall back t using T as the type for U.
So I have a tremendous number of template specializations of this template:
template <typename T> // Same
struct foo { // Same
using type_name = T; // Same
foo(const int base) : _base(base) {} // May take other parameters
void func(const T& param) {} // This function signature will be the same but body will differ
int _base; // Same but may have more members
}; // Same
So an example specialization would be:
template<>
struct foo<float> {
using type_name = T;
foo(const int base, const int child) : _base(base), _child(child) {}
void func(const T& param) { cout << param * _child << endl; }
int _base;
int _child;
};
Obviously this is a toy example and the body of _func will be more involved. But I think this expresses the idea. I can obviously make a macro to help with the boilerplate and put the implementation of the specialized version of the function in an implementation file.
But I was hoping that C++ provided me a way to do this without macros. Is there another way for me avoid writing the boilerplate over and over?
you can have multiple specialization for the function but not for the whole class
like this
#include <iostream>
#include <string>
template<typename T>
struct foo {
//common generic code
using type_name = T;
foo(const int base, const int child) : _base(base), _child(child) {}
void func(const T& param);
int _base;
int _child;
};
template<>
void foo<float>::func(const type_name&) {
//implementation
std::cout << "float" << std::endl;
}
template<>
void foo<int>::func(const type_name&) {
//implementation
std::cout << "int" << std::endl;
}
int main() {
foo<int> tint(0, 0);
foo<float> fint(0, 0);
tint.func(0);
fint.func(0);
}
You can use some light inheritance of data structs to help you separate the differences in member layout and constructor definitions from the main template.
//Define an internal aggregate type you can specialize for your various template parameters
template <typename T>
struct foo_data {
foo(const int base) : _base(base) {}
int _base;
};
//Then derive privately from the data struct (or publicly if you really desire)
template <typename T>
struct foo : private foo_data<T> {
using type_name = T;
using foo_data<T>::foo_data<T>; //Make the base class constructors visible
void func(const T& param); //Use member specialization as suggested by the other answer
};
I will leave it to you to decide if it is better this way or not, but the upshot is that all the common parts are completely separated from all the uncommon parts.
In a comment under another answer I erroneously described this as CRTP. It isn't and it doesn't have any of the drawbacks as CRTP.
If you really need to preserve standard layout, then you can simulate inheritance manually with explicit delegation and perfect forwarding.
template <typename T>
struct foo {
using type_name = T;
template <typename... Args>
foo(Args&&... args) : base_data_(std::forward<Args>(args)...) {}
void func(const T& param); //Use member specialization as suggested by the other answer
foo_data<T> base_data_;
};
One drawback is I don't think the delegating constructor will SFINAE properly as written, and it also eats noexcept specifiers and explicit. Fixing those issues(if required) is left as an exercise to the reader.
There is no nice way to avoid some redundancy in notation when implementing specializations of templated types. There are some techniques to avoid duplication of actual code, such as
Using a traits template to provide type-specific things
template<typename T>
struct foo_traits { ... }; // provide many specialisations
template<typename T> // no specialisations
struct foo
{
using traits = foo_traits<T>;
template<typename...Aars>
explicit foo(Args&&...args)
: data(std::forward<Args>(args)...) {}
int do_something_specific(T x)
{ return traits::do_something(data,x); }
private:
typename traits::data data;
};
a very similar approach is to use a specialized base class:
template<typename T>
struct foo_base { ... }; // provide many specialisations
template<typename T> // no specialisations
struct foo : foo_base<T>
{
using base = foo_base<T>;
template<typename...Aars>
explicit foo(int m, Args&&...args)
: base(std::forward<Args>(args)...)
, more_data(m) {}
int do_something_specific(T x)
{ return base::do_something(x,more_data); }
private:
int more_data;
};
The constructor of foo is a variadic template in order to allow the base class's constructor to take any number and type of arguments.
Of you can use a common base class and specialize the derived classes. This can be done with the Curiously recurring template pattern (CRTP)
template<typename Derived>
struct foo_base // no specializations
{
using type = typename Derived::type;
int do_something(type x)
{
auto result = static_cast<Derived*>(this)->specific_method(x);
return do_some_common_stuff(result);
}
protected:
foo_base(type x) : data(x) {}
type data;
private:
int do_some_common_stuff(type x)
{ /* ... */ }
};
template<typename T> // some specialisations
struct foo : foo_base<foo<T>>
{
using base = foo_base<foo>;
using type = T;
using common_type = typename base::common_type;
using base::do_something;
explicit foo(type x, type y)
: base(x), extra_data(y) {}
protected:
type specific_method(type x)
{ /* ... */ }
private:
type extra_data;
};
Note that foo_base is already a template (unlike the situation with ordinary polymorphism), so you can do a lot of specific stuff there already. Only things that are done differently (not merely with different types) need specializations of foo.
Finally, you can combine these approaches, for example traits classes with CRTP.
All these methods implement some type of static or compile-time polymorphism, rather than real or dynamic polymorphism: there are no virtual functions and hence no virtual table and no overhead for table look-up. It is all resolved at compile time.
This is usually done through inheritance - you put the immutable part into base class, and specialize the children.
I do not think you need an example for that, but let me know if you do.
I have a template class that can (and sometimes has to) take a const type, but there is a method that returns a new instance of the class with the same type, but should be explicitly non-const. For example, the following code fails to compile
template<class T> class SomeClass {
public:
T val;
SomeClass(T val) : val(val) {}
SomeClass<T> other() {
return SomeClass<T>(val);
}
};
int main() {
SomeClass<const int> x(5);
SomeClass<int> y = x.other();
return 0;
}
because even though there's a copy on val during the constructor, it's copying to the same type - const int. Just like you can distinguish between T and const T in a template, is there a way to distinguish between T and "nonconst T"?
SomeClass<typename std::remove_const<T>::type> other()
{
return SomeClass<typename std::remove_const<T>::type>(val);
}
std::remove_const is from <type_traits> and is C++11. There's probably a boost::remove_const in Boost.TypeTraits, or you can even roll your own. It's also possible to use std::remove_cv.
You can either use std::remove_const if you are using c++ 11. Otherwise, you can use this:
struct <typename T>
struct remove_const
{
typedef T type;
};
struct <typename T>
struct remove_const<const T>
{
typedef T type;
};
Which does the same.
The problem in essence, is that there are two distinct types which are not strictly convertible.
You can use/return std::remove_const from type_traits:
#include <type_traits>
template<class T>
class SomeClass {
public:
T val;
SomeClass(T p) : val(p) {
}
SomeClass<typename std::remove_const<T>::type> other() {
return static_cast<SomeClass<typename std::remove_const<T>::type> >(val);
}
};
int main() {
SomeClass<const int>x(5);
SomeClass<int>y = x.other();
return 0;
}
I have a struct like
template<typename T>
struct S
{
T value;
void Set(const T& val) { value = val; }
void Foo();
}
T can be int, float, char, short and long long or one of N other struct-based PODs.
There are about 50 or so PODs and they look something like:
struct POD1 { int i; char c; double d; }
struct POD2 { char c; double d; }
struct POD3 { POD1 p1; char s[10]; }
I'm wondering how to best structure this arrangement. If I wanted the general T case to handle the PODs, would I necessarily need to provide explicit, concrete definitions of the int, float, char, short and long long cases?
Thank you in advance.
First off, Set() is fine for any fundamental type, POD, or aggregate class type, as the latter class types will have a default assignment operator which Does What You Need.
The only question is how to call Foo(). Happily, we have type traits to deal with that, namely std::is_fundamental.
#include <type_traits>
template <typename T> struct S
{
T val;
// Base case: call member `Foo()`.
template <typename U, bool Primitive, bool Array> struct callFoo
{
static void call(const U & u) { u.Foo(); }
};
// Specialization for primitive types (implement this yourself)
template <typename U> struct callFoo<U, true, false>
{
static void call(U u) { /* fundamental implementation here */ }
};
// Specialization for arrays: call `Foo()` on every element.
template <typename U> struct callFoo<U, false, true>
{
typedef typename std::remove_extent<U>::type V;
template <std::size_t N>
static void call(const V (&arr)[N])
{
for (std::size_t i = 0; i != N; ++i)
{
callFoo<V, std::is_fundamental<V>::value, std::is_array<V>::value>::call(arr[i]);
}
}
};
void Foo()
{
callFoo<T, std::is_fundamental<T>::value, std::is_array<T>::value>::call(val);
}
};
(Some minor things: You might want to make callFoo private. And think about constness, too: if applicable, make callFoo and Foo` constant.)