I am trying to conditionally enable a constructor template. With a fully C++11-compliant compiler, I know how to do this using an extra default template argument. However, I need to support VS2012, which has std::enable_if but does not support defaulted function template arguments.
With C++11, I would write the following:
template<typename T>
struct Class
{
template<typename O,
typename = typename std::enable_if<std::is_convertible<O*, T*>::value>::type>
Class(O*) {}
};
I tried the following, but it gives an error C4336 and various follow-up errors:
template<typename T>
struct Class
{
template <typename O>
Class(O*, typename std::enable_if<std::is_convertible<O*, T*>::value>::type *= nullptr)
{
}
};
Is there any way to make this work with VS2012?
Addition:
The usage of the class would be as follows:
struct X { };
struct X2 : X { };
struct Y { };
struct Client
{
Client(Class<X> x) {}
Client(Class<Y> y) {}
};
void test() {
X2* x2;
Client client(x2); // error C2668: ambiguous call to overloaded function
// (without std::enable_if)
}
You are so close to the solution !
template<typename T>
struct Class
{
template <typename O>
Class(O*, typename std::enable_if<std::is_convertible<O*, T*>::value>::type * = nullptr)
{
}
};
Did you spot the difference ? *= inside the parameter list was parsed as the multiplication/assignment operator, not as a pointer type followed by a default argument. Hence, syntax errors.
This is because the C++ parser is specified to consume as many characters as possible when it forms a token (the so-called Maximum Munch Rule). Adding a space splits it into two separate tokens, as intended.
I am afraid, you'd have to use a helper construct function (I didn't find a way around it). But something like this should work:
#include <type_traits>
template<typename T>
struct Class
{
template<typename O>
Class(O* o) { construct(o, std::integral_constant<bool, std::is_convertible<T*, O*>::value>()); }
template<class O>
void construct(O*, std::true_type ) { /* convertible */ }
template<class O>
void construct(O*, ... ) { /* not convertible */ }
};
struct X { };
struct Y : public X { };
void check() {
X x;
int i;
Class<Y> cl(&x);
Class<Y> cl1(&i);
}
Since C++11, you can also use delegating constructors to do that:
template<typename T>
class Class {
template<typename O>
Class(O *o, std::true_type) {}
template<typename O>
Class(O *o, std::false_type) {}
public:
template<typename O>
Class(O *o): Class(o, typename std::is_convertible<O*, T*>::type) {}
};
The basic idea is the one of tag dispatching and maybe it works fine also with VS2012.
See here for further details.
Related
Let's say I want to create a variadic interface with different overloads for the structs A,B,C:
struct A{};
struct B{};
struct C{};
template <typename ... Ts>
class Base;
template <typename T>
class Base<T>{
public:
virtual void visit(const T& t) const
{
// default implementation
}
};
template<typename T, typename ... Ts>
class Base<T, Ts...>: Base<T>, Base<Ts...>{
public:
using Base<T>::visit;
using Base<Ts...>::visit;
};
int main()
{
A a;
B b;
auto base = Base<A,B,C>{};
auto base2 = Base<A,C,B>{};
base.visit(a);
base2.visit(b);
}
Now funtionally Base<A,B,C> is identical to Base<A,C,B> but the compiler still generates the different combinations. Of course with more template parameters it gets worse.
I assume there is some meta programming magic which can cut this code bloat down.
One solution might be to define template<typename T, typename U> Base<T,U> in a way that it checks if Base<U,T> already exists. This could reduce at least some combinations and can probably be done by hand for triplets as well. But I am missing some meta programming magic and hoping for a more general approach.
Edit:
I would like to have the variadic Interface for a (simplified) use case like that:
class Implementation:public Base<A,B,C>
{
public:
void visit(const A& a) const
{
std::cout <<"Special implementation for type A";
}
void visit(const B& a) const
{
std::cout <<"Special implementation for type B";
}
// Fall back to all other types.
};
using BaseInterface = Base<A,B,C>;
void do_visit(const BaseInterface& v)
{
v.visit(A{});
v.visit(B{});
v.visit(C{});
}
int main()
{
std::unique_ptr<BaseInterface> v= std::make_unique<Implementation>();
do_visit(*v);
}
The reason why I want to do this is that there could be potentially a lot of types A,B,C,... and I want to avoid code duplication to define the overload for each type.
Base<A, B, C> instantiates Base<A>, Base<B, C>, Base<B>, Base<C>
and
Base<A, C, B> instantiates Base<A>, Base<C, B>, Base<B>, Base<C>
Whereas final nodes are needed, intermediate nodes increase the bloat.
You can mitigate that issue with:
template <typename T>
class BaseLeaf
{
public:
virtual ~BaseLeaf() = default;
virtual void visit(const T& t) const
{
// default implementation
}
};
template <typename... Ts>
class Base : public BaseLeaf<Ts>...
{
public:
using BaseLeaf<Ts>::visit...;
};
Demo
Base<A,B,C> and Base<A,C,B> are still different types.
To be able to have same type, they should alias to the same type, and for that, ordering Ts... should be done in a way or another.
Looks like the member function should be a template rather than the class.
struct A{};
struct B{};
struct C{};
class Foo {
public:
template<typename T>
void visit(const T& t) const
{
// default implementation
}
};
int main()
{
A a;
B b;
auto foo = Foo{};
foo.visit(a);
foo.visit(b);
}
https://godbolt.org/z/nTrYY6qcn
I'm not sure what is your aim, since there is not enough details. With current information I think this is best solution (there is a also a lambda which can address issue too).
It's necessary to enforce some sort of discipline on the order of template parameters. You can do this with a template variable and a few static_asserts:
#include <type_traits>
template <typename ... Ts>
class Base;
struct A
{
};
struct B
{
};
struct C
{
};
struct D
{
};
template <class T>
static constexpr int visit_sequence_v = -1;
template <>
constexpr int visit_sequence_v<A> = 0;
template <>
constexpr int visit_sequence_v<B> = 1;
template <>
constexpr int visit_sequence_v<C> = 2;
template <typename T>
class Base<T>{
public:
static_assert(visit_sequence_v<T> >= 0, "specialize visit_sequence_v for this type");
virtual void visit(const T& t) const
{
// do nothing by default
}
};
template<typename T1, typename T2>
class Base<T1, T2>: Base<T1>, Base<T2>
{
public:
static_assert(std::is_same_v<T1, T2> || visit_sequence_v<T1> < visit_sequence_v<T2>);
using Base<T1>::visit;
using Base<T2>::visit;
};
template<typename T1, typename T2, typename ... Ts>
class Base<T1, T2, Ts...>: Base<T1>, Base<T2, Ts...>
{
public:
static_assert(std::is_same_v<T1, T2> || visit_sequence_v<T1> < visit_sequence_v<T2>);
using Base<T1>::visit;
using Base<Ts...>::visit;
};
int main()
{
A a;
B b;
auto base = Base<A,B,C>{};
//auto base2 = Base<A,C,B>{}; // static_assert fails
//auto base3 = Base<A,B,C,D>{}; // forgot to specialize
base.visit(a);
}
Notice the point here is to cause a compilation failure if you get the order wrong. If someone has the chops to do a compile-time sort it may be possible to cobble up a traits class (or a template function that you can use decltype on the return type) that selects an implementation of Base in the correct order.
One alternative is to declare a full specialization of Base for every individual type that can be visited (supplying a "default implementation" for visit) and declare a static constexpr visit_sequence within each specialzation.
A problem inherent in your method is that in the case of multiple inheritance, visit can be ambiguous:
struct E: public A, public B
{
};
// 5 MiNuTES LATeR...
DescendantOfBase<A, B> a_b;
E e;
a_b.visit (e); // ambiguous
What is the best way to expose operators from base class like the following code is trying to do. I suppose with one or two base classes we'd use using syntax, but with a variadic number of base classes, is something like using Base<Ts>::operator=...; possible?
template <typename T, typename Derived>
class varU
{
protected:
varU() = default;
Derived& operator=(T val)
{
static_cast<Derived*>(this)->set(val);
return *static_cast<Derived*>(this);
}
};
template <typename ...Ts>
class var : public varU<Ts, var<Ts...>>...
{
// using varU<Ts, var<Ts...>>::operator=...; // Something like this?
private:
template <typename T>
void set(const T& v)
{
}
};
EDIT:
Seems like using Base<Ts>::operator=... is indeed the correct syntax which I was looking for in C++17. I was using the wrong standard version and expecting C++17. Since this was my guess, I didn't dig deep.
I'm not exactly sure, what you want to achieve with your code, so perhaps you want to go a bit more into detail in your question.
But regarding your question in the code-comment, the answer is yes, it's possible in C++17.
If you actually want to use your operators, you also have to declare the varU class a friend of var:
The following compiles on gcc and clang:
template <typename T, typename Derived>
class varU
{
protected:
varU() = default;
Derived& operator=(T val)
{
static_cast<Derived*>(this)->set(val);
return *static_cast<Derived*>(this);
}
};
template <typename ...Ts>
class var : public varU<Ts, var<Ts...>>...
{
template <typename T, typename Derived>
friend class varU;
public:
using varU<Ts, var<Ts...>>::operator=...; // Something like this?
private:
template <typename T>
void set(const T& v)
{
}
};
int main() {
var<int, float> x;
x = 5;
x = 5.f;
return 0;
}
See live code here.
I'm setting up a variadic template function to be able to call various function overloads on a specific series of classes. So far, I've been able to "break" the compilation when an unsupported class is passed to the function, but I'd like to be able to provide a valid fallback to handle the "unsupported" scenario at runtime.
The current implementation goes like this :
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, typename = std::enable_if_t<is_my_class_v<T>>>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, typename... Ts>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b); // works
run(c); // does not compile
}
Here, the two most promising (but still failing) attempts I've made to have a fallback for run:
1 - Adding a simple ! in front of is_my_class<T>, giving me the following error : error C2995: 'void run(T)': function template has already been defined
template<typename T, typename = std::enable_if_t<!is_my_class_v<T>>>
void run(T myClass)
{
printf("Not supported\n");
};
2 - Making a more "primary" template definition, which yields a sad but obvious : error C2668: 'run': ambiguous call to overloaded function
template<typename T>
void run(T myClass)
{
printf("Not supported\n");
};
EDIT
I forgot to specify I was looking for a solution also compatible with C++11/14
You could avoid enable_if entirely, and just use a compile-time if to to decide what code to execute:
template<typename T>
void run(T myClass)
{
if constexpr (is_my_class_v<T>)
runOverload(myClass);
else
printf("Not supported\n");
}
Here's a demo.
Even though I'd recommend #cigien 's solution if c++17 is avaiable, I would like to add, that the ambiguity problem can be mitigated pre c++17 by changing the type of the enable_if template argument, and hence changing the signature of the "fallback function". The following code should work fine:
template<typename T, std::enable_if_t<!is_my_class_v<T>, int> = 0>
//template<typename T>
void run(T myClass) {
printf("error\n");
};
Full code available on CompilerExplorer, tested on GCC and Clang trunk.
I'd also like to add, that in all use cases I can imagine, it is better to have a compile time error (which you could for instance achieve by using a static assertion instead of SFINAE).
Here you can read about why the function delcaration is not ambiguous, even when using two "ints" as template arguments.
I wrote following code and it works. I think you just messed up with syntax of SFINAE.
#include <iostream>
#include <type_traits>
struct ClassA {};
struct ClassB {};
struct ClassC {};
template<typename T> struct is_my_class : std::false_type {};
template<> struct is_my_class<ClassA> : std::true_type {};
template<> struct is_my_class<ClassB> : std::true_type {};
template<typename T>
constexpr bool is_my_class_v = is_my_class<T>::value;
void runOverload(ClassA c) { printf("ClassA overload\n"); }
void runOverload(ClassB c) { printf("ClassB overload\n"); }
template<typename T, std::enable_if_t<is_my_class_v<T>,int> = 0>
void run(T myClass)
{
runOverload(myClass);
};
template<typename T, std::enable_if_t<not is_my_class_v<T>,int> = 0>
void run(T myClass)
{
printf("Not supported\n");
};
// wrote an extra SFINEA here to ensure that Ts aren't empty - else it might be an ODR issue despite the fact that it compiles
template<typename T, typename... Ts, std::enable_if_t<sizeof...(Ts) != 0,int> = 0>
void run(T myClass, Ts... classesLeft)
{
run(myClass);
run(classesLeft...);
};
int main()
{
ClassA a;
ClassB b;
ClassC c;
run(a, b);
run(c);
}
It prints
ClassA overload
ClassB overload
Not supported
Have a fallback runOverload template
template<typename T>
void runOverload(T myClass)
{
printf("Not supported\n");
};
Given I have a template setup to do something on a type such as...
template<typename T>
class SimpleTemplate
{
private:
T m_obj;
public:
void operator()() { m_obj.DoSomething(); }
};
And I want to handle the case where I have a collection of type T the same way. I currently have a template setup like so for a vector...
template<typename T>
class SimpleTemplate<std::vector<T>>
{
private:
std::vector<T> m_collection;
public:
void operator()()
{
for (auto&& obj : m_collection) obj.DoSomething();
}
};
Now I want to also support sets, unordered_sets and so on. I could write a template for each collection but I feel like this should be a perfect job for a template, only I can't figure out how it should be written, or even if it can be?
Can I do something like template<typename C<T>>?
As mentioned by Geoffroy, you can use a trait to detect whether T can be iterated over. You then use this to select the correct specialization.
So start off with the "is_iterable" trait shown by Jarod42 here:
// Code by Jarod42 (https://stackoverflow.com/a/29634934).
#include <iterator>
#include <type_traits>
namespace detail
{
// To allow ADL with custom begin/end
using std::begin;
using std::end;
template <typename T>
auto is_iterable_impl(int)
-> decltype (
begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
void(), // Handle evil operator ,
++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
void(*begin(std::declval<T&>())), // operator*
std::true_type{});
template <typename T>
std::false_type is_iterable_impl(...);
}
template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));
This gives you an is_iterable<T> trait which inherits from either std::true_type or std::false_type. Now use this with SFINAE to create two specializations:
template <class T, bool = is_iterable<T>::value>
class SimpleTemplate;
template <class T>
class SimpleTemplate<T, false> {
private:
T m_obj;
public:
SimpleTemplate (T obj) : m_obj(std::move(obj)) { }
void operator() () { m_obj.DoSomething(); }
};
template <class T>
class SimpleTemplate<T, true> {
private:
T m_collection;
public:
SimpleTemplate (T obj) : m_collection(std::move(obj)) { }
void operator() () {
for (auto && obj : m_collection) { obj.DoSomething(); }
}
};
Since both partial specializations are mutually exclusive for any given T, you won't get any errors about ambiguity.
Edit: Changed 2nd template argument into a bool instead of class. This makes it simple to fully specialize it in case the default behavior is unwanted.
E.g. for std::string, which for which is_iterable is true, simply do the following. Note that I added constructors to SimpleTemplate, I couldn't get the full specialization to inherit the base class' constructor otherwise.
template <>
class SimpleTemplate<std::string, true>
: public SimpleTemplate<std::string, false> {
// Inherit constructor.
using base = SimpleTemplate<std::string, false>;
using base::base;
};
Now I want to also support sets, unordered_sets and so on. I could write a template for each collection but I feel like this should be a perfect job for a template, only I can't figure out how it should be written, or even if it can be
Maybe you can use a template-template parameter
template <template <typename...> class C, typename... Ts>
class SimpleTemplate<C<Ts...>>
{
private:
C<Ts...> m_collection;
public:
void operator()()
{
for (auto&& obj : m_collection) obj.DoSomething();
}
};
This should intercept std::(unordered_)(multi)set, std::vector, std::deque, etc.
Unfortunately doesn't intercept std::array, because it's second template parameter is a value, not a type.
I am trying to write the "promotion" constructor of a nested class that can deduce the parent class template. It works fine for the parent class, but not in the nested class. Here is a code example.
template <class T>
struct potato {
struct baked {
template <class O>
baked(const typename potato<O>::baked& p)
: something(static_cast<T>(p.something)) {
}
baked() = default;
T something;
};
template <class O>
potato(const potato<O>& p)
: mybaked(p.mybaked) {
}
potato() = default;
baked mybaked;
};
int main(int, char**) {
potato<int> potato1;
potato<short> potato2(potato1);
}
Is this legal?
Various compilers output various errors. Clang has the most readable in my mind. It states :
candidate template ignored: couldn't infer template argument 'O'
https://godbolt.org/z/y_IZiE
So I'm guessing either I've messed up the signature, or this isn't a c++ supported feature.
I don't know of any way to deduce the template argument T for a baked's parent potato<T>. You can know T using decltype(p.something) but that doesn't seem to help solve the problem with calling the constructor. One workaround is to change baked's constructor to take any O and assume it has a something :
struct baked {
template <class O>
baked(const O & p) : something(static_cast<T>(p.something))
{ }
baked() = default;
T something;
};
This will work but it is less type-safe than your original code seems to intend. One workaround for that problem could be to introduce a static_assert that checks that O is actually a potato<U>::baked :
#include <type_traits>
template <class T>
struct potato {
struct baked {
template <class O>
baked(const O & p) : something(static_cast<T>(p.something))
{
using t_parent = potato<decltype(p.something)>;
static_assert(std::is_same<O, typename t_parent::baked>::value, "Not a baked potato!");
}
baked() = default;
T something;
};
template <class O>
potato(const potato<O>& p)
: mybaked(p.mybaked) {
}
potato() = default;
baked mybaked;
};
This should compile fine for the intended usage but fail with "Not a baked potato!" if you try to pass anything else with a something. This would fail :
struct foo {
int something = 0;
};
int main(int, char**) {
foo bar;
potato<int>::baked baz(bar); // Error: Not a baked potato!
}
As state by the compiler O is not deducible from const typename potato<O>::baked& (on left side of ::).
You have several workarounds:
Move baked outside parent and make it template:
// Possibly in namespace details
template <typename T>
struct baked_impl {
template <class O>
baked_impl(const typename baked_impl<O>& p)
: something(static_cast<T>(p.something)) {
}
baked_impl() = default;
T something;
};
template <class T>
struct potato {
using baked = baked_impl<T>;
// ...
};
Add parent info in baked and use SFINAE:
template <class T> struct potato;
// traits for SFINAE
template <class T> struct is_potato : std::false_type {};
template <class T> struct is_potato<potato<T>> : std::true_type {};
template <class T>
struct potato {
using value_type = T;
struct baked {
using parent = potato;
template <class O, std::enable_if_t<is_potato<typename O::parent>::value, int> = 0>
baked(const O& p)
: something(static_cast<typename O::parent::value_type>(p.something)) {
}
baked() = default;
T something;
};
// ...
};