trouble with constructing from initializer_list - c++

I have a class which shall have many different constructor overloads, one of which is from an initializer_list. Unfortunately, this will be picked automatically when I use list-initialisation as in
class foo
{
template<typename T>
foo(T const&it);
template<typename T>
foo(std::initializer_list<T> list);
};
template<typename other>
foo bar(other const&it)
{
return {it};
}
when the second constructor is called, not the first. This is correct but counter intuitive and hence dangerous. How can I prevent that? I would be happy with a solution where the second constructor specialises for the case that list.size()==1. For example
template<typename T>
foo::foo(std::initializer_list<T> list)
: foo(list.begin(), list.size()) {}
template<typename T> // private constructor
foo::foo(const T*p, size_t n)
{
if(n==1)
foo::foo(p[0]); // this does not what I want
else {
/* ... */
}
}
where I try to explicitly call the first constructor from within another constructor. Is this possible? (the code compiles, but doesn't appear to call the intended constructor or in fact any constructor). The only way I could make this work was by using placement new:
template<typename T> // private constructor
foo::foo(const T*p, size_t n)
{
if(n==1)
::new(this) foo(p[0]); // seems to work
else {
/* ... */
}
}
However, this is at least inelegant if not outright dangerous. Are there better solutions?
Note that trying to replicate the effort of the first constructor instead of calling it is not really a useful solution, as there will be many different first constructors for different type of arguments using SFINAE.

Maybe you can use variadic template instead of initializer_list.
Something like:
#include <type_traits>
// equivalent to std::is_same but for more type.
template <typename... Ts> struct are_same : std::false_type {};
template <typename T> struct are_same<T> : std::true_type {};
template <typename T, typename ...Ts> struct are_same<T, T, Ts...> : are_same<T, Ts...> {};
class foo
{
struct dummy_t {};
public:
template <typename...Ts, typename = typename std::enable_if<(sizeof...(Ts) > 1) && are_same<typename std::decay<Ts>::type...>::value>::type>
foo(Ts&&... args);
template<typename T>
foo(T const&it);
};

Related

Avoiding template parameter substitution completely

I have a class that can accept arithmetic types and std::complex. A simplified code of the class is
#include <complex>
template<typename T> struct is_complex : std::false_type {};
template<typename T> struct is_complex<std::complex<T>> : std::true_type {};
template<class T>
struct Foo {
void foo(typename T::value_type t)
requires (is_complex<T>::value) {
}
};
Now, I would like to take the internal type of std::complex and use it as the type of the parameters in the foo function.For example, if T is std::complex<double>, then I want the parameter types to be double.
This function should only be available when T is indeed std::complex.
I thought I could use typename T::value_type as the parameter type, since std::complex has a typedef value_type. Plus, I thought using requires here would avoid T to be substitued in this function in case T wasn't std::complex. Silly me.
The issue is that whenever I create a Foo<FundamentalType> the code breaks, since fundamentals don't have ::value_type.
int main() {
Foo<int> obj; // Breaks the code.
//obj.foo(4); // Function shouldn't be considered in overload resolution ideally...
Foo<std::complex<int>> obj2; // Works
obj2.foo(4); // Works as expected
}
Ideally, I would like the substitution of T to be ignored for this function in case T is not std::complex. Is that possible? If not, how can I circumvent this?
You're on the right track with is_complex: you'd like the same here, but with a different body of the type. For example,
template<typename T> struct complex_value_type {};
template<typename T> struct complex_value_type<std::complex<T>> { using type = T; };
template<typename T>
using complex_value_type_t = typename complex_value_type<T>::type;
Then, at any point, you can call it as complex_value_type_t<T>:
template<class T>
struct Foo {
template<typename T_ = T>
void foo(complex_value_type_t<T_> t)
requires (is_complex<T_>::value) {
}
};
The requires is not absolutely necessary then; it's already covered by complex_value_type_t<T> being defined only for complex<T>.
You just need some type to put in there, until requires can disable the function.
I would do this:
struct nullptr_value_type {using value_type = std::nullptr_t;};
using elem_or_null_t = typename std::conditional_t<is_complex<T>::value, T, nullptr_value_type>::value_type;
void foo(elem_or_null_t t)
requires (is_complex<T>::value)
{}
Use a template class as a template parameter.
#include <complex>
template<template<class> class T> struct is_complex : std::false_type {};
template<> struct is_complex<std::complex> : std::true_type {};
template<template<class> class T>
struct Foo {
void foo(typename T<double>::value_type t)//could be typename<T<TT>> if you made foo a templated function
requires (is_complex<T>::value) {
}
};
int main(){
Foo<std::complex> f;
};
But you will need what to put into it when used. I just hard-coded double but you have to add a new template parameter to Foo or make foo a templated member function

Specializing a template for a container of type T

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.

enable_if and constructor with VS2012

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.

How to use sfinae for selecting constructors?

In template meta programming, one can use SFINAE on the return type to choose a certain template member function, i.e.
template<int N> struct A {
int sum() const noexcept
{ return _sum<N-1>(); }
private:
int _data[N];
template<int I> typename std::enable_if< I,int>::type _sum() const noexcept
{ return _sum<I-1>() + _data[I]; }
template<int I> typename std::enable_if<!I,int>::type _sum() const noexcept
{ return _data[I]; }
};
However, this doesn't work on constructors. Suppose, I want to declare the constructor
template<int N> struct A {
/* ... */
template<int otherN>
explicit(A<otherN> const&); // only sensible if otherN >= N
};
but disallow it for otherN < N.
So, can SFINAE be used here? I'm only interested in solutions which allow automatic template-parameter deduction, so that
A<4> a4{};
A<5> a5{};
A<6> a6{a4}; // doesn't compile
A<3> a3{a5}; // compiles and automatically finds the correct constructor
Note: this is a very simplified example where SFINAE may be overkill and static_assert may suffice. However, I want to know whether I can use SFINAE instead.
You can add a defaulted type argument to the template:
template <int otherN, typename = typename std::enable_if<otherN >= N>::type>
explicit A(A<otherN> const &);
There are many ways to trigger SFINAE, being enable_if just one of them.
First of all:
What is std::enable_if ?
It's just this:
template<bool, class T=void> enable_if{ typedef T type; };
template<class T> enable_if<false,T> {};
template<bool b, class T=void> using enable_if_t = typename enable_f<b,T>::type;
The idea is to make typename enable_if<false>::type to be an error, hence make any template declaration containing it skipped.
So how can this trigger function selection?
Disabling functions
The idea is making the declaration erroneous in some part:
By return type
template<class Type>
std::enable_if_t<cond<Type>::value,Return_type> function(Type);
By a actual parameter
template<class Type>
return_type function(Type param, std::enable_if_t<cond<Type>::value,int> =0)
By a template parameter
template<class Type,
std::enable_if_t<cond<Type>::value,int> =0> //note the space between > and =
return_type function(Type param)
Selecting functions
You can parametrise different alternatives with tricks like this:
tempplate<int N> struct ord: ord<N-1>{};
struct ord<0> {};
template<class T, std::enable_if<condition3, int> =0>
retval func(ord<3>, T param) { ... }
template<class T, std::enable_if<condition2, int> =0>
retval func(ord<2>, T param) { ... }
template<class T, std::enable_if<condition1, int> =0>
retval func(ord<1>, T param) { ... }
template<class T> // default one
retval func(ord<0>, T param) { ... }
// THIS WILL BE THE FUCNTION YOU'LL CALL
template<class T>
retval func(T param) { return func(ord<9>{},param); } //any "more than 3 value"
This will call the first/second/third/fourth function if condition3 is satisfied, than condition2 than condition1 than none of them.
Other SFINAE triggers
Writing compile-time conditions can be either a matter of explicit specialization or a matter of unevaluated expression success/failure:
for example:
template<class T, class = void>
struct is_vector: std::false_type {};
template<class X>
struct is_vector<vector<X> >:: std::true_type {};
so that is_vector<int>::value is false but is_vecttor<vector<int> >::value is true
Or, by means of introspection, like
template<class T>
struct is_container<class T, class = void>: std::false_type {};
template<class T>
struct is_container<T, decltype(
std::begin(std::declval<T>()),
std::end(std::declval<T>()),
std::size(std::declval<T>()),
void(0))>: std::true_type {};
so that is_container<X>::value will be true if given X x, you can compile std::begin(x) etc.
The trick is that the decltype(...) is actually void (the , operator discards the previous expressions) only if all the sub-expressions are compilable.
There can be even many other alternatives. Hope between all this you can find something useful.
The accepted answer is good for most cases, but fails if two such constructor overloads with different conditions are present. I'm looking for a solution in that case too.
Yes: the accepted solution works but not for two alternative constructor as, by example,
template <int otherN, typename = typename std::enable_if<otherN == 1>::type>
explicit A(A<otherN> const &);
template <int otherN, typename = typename std::enable_if<otherN != 1>::type>
explicit A(A<otherN> const &);
because, as stated in this page,
A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.
As proposed in the same page, you can go around this problem applying SFINAE, modifying the signature, to the type of a value (not type) template parameter as follows
template <int otherN, typename std::enable_if<otherN == 1, bool>::type = true>
explicit A(A<otherN> const &);
template <int otherN, typename std::enable_if<otherN != 1, bool>::type = true>
explicit A(A<otherN> const &);
In C++11, you can use a defaulted template parameter:
template <int otherN, class = typename std::enable_if<otherN >= N>::type>
explicit A(A<otherN> const &);
However, if your compiler doesn't support defaulted template parameters yet, or you need multiple overloads, then you can use a defaulted function parameter like this:
template <int otherN>
explicit A(A<otherN> const &, typename std::enable_if<otherN >= N>::type* = 0);
With C++20 you can use the requires keyword
With C++20 you can get rid of SFINAE.
The requires keyword is a simple substitute for enable_if!
Note that the case where otherN == N is a special case, as it falls to the default copy ctor, so if you want to take care of that you have to implement it separately:
template<int N> struct A {
A() {}
// handle the case of otherN == N with copy ctor
explicit A(A<N> const& other) { /* ... */ }
// handle the case of otherN > N, see the requires below
template<int otherN> requires (otherN > N)
explicit A(A<otherN> const& other) { /* ... */ }
// handle the case of otherN < N, can add requires or not
template<int otherN>
explicit A(A<otherN> const& other) { /* ... */ }
};
The requires clause gets a constant expression that evaluates to true or false deciding thus whether to consider this method in the overload resolution, if the requires clause is true the method is preferred over another one that has no requires clause, as it is more specialized.
Code: https://godbolt.org/z/RD6pcE

Is is possible to create a composite constructor without tuples?

I know that I can expand a parameter pack of tuples onto a variadic template of base classes
like this:
#include <tuple>
struct ComponentA {
int foo;
int bar;
ComponentA(std::tuple<int, int> && pack):
foo(std::get<0>(pack)),
bar(std::get<1>(pack))
{}
};
struct ComponentB {
ComponentB(std::tuple<> && pack)
{}
};
template<typename ... Bases>
struct CompositeClass: public Bases... {
template<typename ... Arguments>
CompositeClass(Arguments &&... arguments):
Bases(std::forward<Arguments>(arguments))...
{}
};
int main() {
CompositeClass<ComponentA, ComponentB> composite{
std::make_tuple(100, 100),
std::make_tuple()
};
}
However, I find that syntax cumbersome. Is there a way to avoid the tuples altogether?
Edit:
Something more like this:
struct ComponentA {
int foo;
int bar;
ComponentA(int a, int b):
foo(a),
bar(b)
{}
};
struct ComponentB {
ComponentB()
{}
};
template<typename ... Bases>
struct CompositeClass: public Bases... {
template<typename ... ... Arguments>
CompositeClass(Arguments &&... ... arguments):
Bases(std::forward<Arguments>(arguments)...)...
{}
};
int main() {
CompositeClass<ComponentA, ComponentB> composite{100, 100};
}
Both parameters are passed to ComponentA, none to ComponentB.
Edit 2
So I have got something like this:
template <int ...>
struct SequenceContainer {};
template <int, typename>
struct AppendIntToSequence;
template <int Value, int ... Sequence>
struct AppendIntToSequence<Value, SequenceContainer<Sequence...>> {
typedef SequenceContainer<Sequence..., Value> type;
};
template<int End>
struct MakeSequence:
AppendIntToSequence<End - 1, typename MakeSequence<End - 1>::type> {};
template<>
struct MakeSequence<0> {
typedef SequenceContainer<> type;
};
struct ComponentA {
static const int parameters = 2;
ComponentA(int && a, int && b) {
std::cout << a << b << std::endl;
}
};
struct ComponentB {
static const int parameters = 1;
ComponentB(const char * string) {
std::cout << string << std::endl;
}
};
template <typename Base>
struct TupleConstructor: Base {
template <typename ... Arguments, int ... Sequence>
TupleConstructor(std::tuple<Arguments...> &&, SequenceContainer<Sequence...> const &);
};
template <typename Base>
template <typename ... Arguments, int... Sequence>
TupleConstructor<Base>::TupleConstructor(std::tuple<Arguments...> && arguments, SequenceContainer<Sequence...> const &):
Base(std::forward<Arguments>(std::get<Sequence>(arguments))...)
{}
template <typename ... Components>
struct CompositeClass: public TupleConstructor<Components>... {
template <typename ... Arguments>
CompositeClass(Arguments &&... arguments):
TupleConstructor<Components>(
std::forward<Arguments>(arguments),
typename MakeSequence<std::tuple_size<Arguments>::value>::type{}
)...
{}
};
int main()
{
CompositeClass<ComponentA, ComponentB> composite{
std::tuple<int &&, int &&>(100,100),
std::tuple<const char *>("Hello World!")
};
However, I haven't been able to figure out how to remove the two tuples from the CompositeClass constructor. How can this be done?
It seems, if you cut down a bit on sprinkling periods all over the place you should be OK! Parameter packs aren't anything particular special: they are just expanded into a comma separate list. That is, if you change your code to become that below you should be OK:
template <typename... Bases>
struct CompositeClass: public Bases... {
template <typename... Arguments>
CompositeClass(Arguments&&... arguments):
Bases(std::forward<Arguments>(arguments))...
{}
};
Except for adding a colon in front of the initializer list I have only removed some excess "...". This works OK as long as all your template arguments are actually class types and as long as they happen to be different. Obviously, types which aren't classes can't be used as bases. If You need the same type multiple times as base you need to inherit them indirectly why an auxiliary type which gives them a number. Generating the numbers is a bit tricky the first few times you do it but nothing really magically either.
Expanding on the edited question: You mean, you want to pass lists of arguments to each individual base class constructor? If it is OK to pass in std:tuple<...> as arguments to your CompositeClass this is doable, too. Essentially, you need to transform each std::tuple<...> into a list of arguments. This also requires the generation of said indices. I would at least start off with an auxiliary base which takes a std::tuple<...> as argument and forwards the members as arguments to the base. This would use something similar to the code above for the CompositeClass and the main trick would be in an auxiliary class:
template <int... Numbers> struct Sequence {};
template <int N> struct MakeSequence;
template <typename Base>
struct AuxiliaryBase: Base {
template <typename Tuple, int... Numbers>
AuxiliaryBase(Tuple&&, Sequence<Numbers...> const&);
};
template <typename... Bases>
struct CompositeClass: public AuxiliaryBase<Bases>... {
template <typename... Args>
CompositeClass(Args&&... arguments):
AuxiliaryBase<Bases>(std::forward<Args>(arguments),
typename MakeSequence<std::tuple_size<Args>::size>())...
{}
};
Implementing the constructor of AuxiliaryBase essentially requires that there is a facility creating a sequence of integers from 0 to std::tuple_size<Tuple>::value. This take a bit tweaking but is definitely doable. To make them available an auxiliary parameter is passed in (I'm not sure if this can be avoided; well, probably it can be packaged as a type with the std::tuple<...> if this should be avoided). With this, the base class constructor is fairly straight forward:
template <typename Base>
template <typename Tuple, int... Numbers>
AuxiliaryBase<Base>::AuxiliaryBase(Tuple&& tuple, Sequence<Numbers...> const&):
Base(std::get<Numbers>(tuple)...)
{}
I haven't tested it but something along these lines should actually work. What this version doesn't do is perfect forwarding of the members in the std::tuple<...>: for this you would need a variation of std::forward<>() which takes three arguments which uses the reference qualifications of the outer type to determine which kind of reference needs to be forward for the member. I haven't tried this but it is probably an interesting exercise as well.
I haven't tried to compile this particular example but I have definitely done something like this in the past: if you look the slides for the presentation I gave at the ACCU conference in 2010 you will find all the details on how to do this (including how to create a sequence of integers; a very short of this, actually).