Update: conditional explicit has made it into the C++20 draft. more on cppreference
The cppreference std::tuple constructor page has a bunch of C++17 notes saying things like:
This constructor is explicit if and only if std::is_convertible<const Ti&, Ti>::value is false for at least one i
How can one write a constructor that is conditionally explicit? The first possibility that came to mind was explicit(true) but that's not legal syntax.
An attempt with enable_if was unsuccessful:
// constructor is explicit if T is not integral
struct S {
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
S(T) {}
template <typename T,
typename = typename std::enable_if<!std::is_integral<T>::value>::type>
explicit S(T) {}
};
with the error:
error: ‘template<class T, class> S::S(T)’ cannot be overloaded
explicit S(T t) {}
The proposal that added that N4387: Improving pair and tuple, revision 3 has an example of how it works:
Consider the following class template A that is intended to be used
as a wrapper for some other type T:
#include <type_traits>
#include <utility>
template<class T>
struct A {
template<class U,
typename std::enable_if<
std::is_constructible<T, U>::value &&
std::is_convertible<U, T>::value
, bool>::type = false
>
A(U&& u) : t(std::forward<U>(u)) {}
template<class U,
typename std::enable_if<
std::is_constructible<T, U>::value &&
!std::is_convertible<U, T>::value
, bool>::type = false
>
explicit A(U&& u) : t(std::forward<U>(u)) {}
T t;
};
The shown constructors both use perfect forwarding and they have
essentially the same signatures except for one being explicit, the
other one not. Furthermore, they are mutually exclusively constrained.
In other words: This combination behaves for any destination type T
and any argument type U like a single constructor that is either
explicit or non-explicit (or no constructor at all).
As Praetorian points out this is exactly how libstdc++ implements it.
If we modify the OPs example accordingly, it also works:
struct S {
template <typename T,
typename std::enable_if< std::is_integral<T>::value, bool>::type = false>
S(T) {}
template <typename T,
typename std::enable_if<!std::is_integral<T>::value, bool>::type = false>
explicit S(T) {}
};
One way that seems to work with most compilers is to add a dummy parameter to one of the functions, to make them slightly different.
// constructor is explicit if T is integral
struct S {
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
S(T t) {}
template <typename T,
typename = typename std::enable_if<!std::is_integral<T>::value>::type,
typename dummy = void>
explicit S(T t) {}
};
int main()
{
S s1(7);
S s2("Hello");
}
Compiles with MSVC 2015.
Related
In template metaprogramming, there is a trick:
template <typename T>
struct my_struct {};
template <typename... T1, typename... Ts>
struct my_struct <std::tuple<T1, Ts...>>
{
// Tuple is "unpacked"
// I can freely work on 'T1' & 'Ts' types (through recursion) in this scope.
using type = T1;
};
I was hoping that a similar trick exists for member functions (especially constructor):
template <typename T>
struct my_struct {};
// specialisation for case when type 'T' has 'T(U1, Us...)' ctor.
template <typename T, typename U1, typename... Us>
struct my_struct <
decltype(T::T(
std::declval<U1>(),
std::declval<Us>()...
))
>
{
using type = U1;
};
unfortunately, this results in an error:
template parameter "U1" & parameter pack "Us" is not used in or cannot be deduced from the template argument list of class template "my_struct"
Is there any way to get the types of T's ctor arguments?
I need the above because I have a function func<T>() which must be "specialized" (/"overloaded") for a situation when Type T has T(std::initializer_list</* ??? */>) constructor.
template<typename T>
struct get_init_list_type_from_T_ctor
{
// magic happens here
};
template<typename T>
using has_init_list_ctor = std::enable_if_t<
std::is_constructible_v<
T,
std::initializer_list<
typename get_init_list_type_from_T_ctor<T>::type
>
>
>;
// version for T with initialization list ctor
template<
typename T,
typename = std::enable_if_t<
std::is_detected_v<has_init_list_ctor, T>
>
>
void func() {
//...
}
// version for T without initialization list ctor
template<
typename T,
typename = std::enable_if_t<
!std::is_detected_v<has_init_list_ctor, T>
>
>
void func() {
//...
}
It is not possible to enforce a specific constructor signature in C++.
However, if the type of the initializer list is fixed, you can still utilize SFINAE by calling the constructor with the required arguments:
template <typename T, typename U>
struct has_init_list_ctor
: std::is_constructible<T, std::initializer_list<U>>
{
};
template <typename T, typename U>
inline constexpr bool has_init_list_ctor_v = has_init_list_ctor<T, U>::value;
This won't work if you don't know the type of the initializer list. We have to wait for the Reflection TS. If that is the case, I can't think of how this can possibly be useful, though.
Update: conditional explicit has made it into the C++20 draft. more on cppreference
The cppreference std::tuple constructor page has a bunch of C++17 notes saying things like:
This constructor is explicit if and only if std::is_convertible<const Ti&, Ti>::value is false for at least one i
How can one write a constructor that is conditionally explicit? The first possibility that came to mind was explicit(true) but that's not legal syntax.
An attempt with enable_if was unsuccessful:
// constructor is explicit if T is not integral
struct S {
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
S(T) {}
template <typename T,
typename = typename std::enable_if<!std::is_integral<T>::value>::type>
explicit S(T) {}
};
with the error:
error: ‘template<class T, class> S::S(T)’ cannot be overloaded
explicit S(T t) {}
The proposal that added that N4387: Improving pair and tuple, revision 3 has an example of how it works:
Consider the following class template A that is intended to be used
as a wrapper for some other type T:
#include <type_traits>
#include <utility>
template<class T>
struct A {
template<class U,
typename std::enable_if<
std::is_constructible<T, U>::value &&
std::is_convertible<U, T>::value
, bool>::type = false
>
A(U&& u) : t(std::forward<U>(u)) {}
template<class U,
typename std::enable_if<
std::is_constructible<T, U>::value &&
!std::is_convertible<U, T>::value
, bool>::type = false
>
explicit A(U&& u) : t(std::forward<U>(u)) {}
T t;
};
The shown constructors both use perfect forwarding and they have
essentially the same signatures except for one being explicit, the
other one not. Furthermore, they are mutually exclusively constrained.
In other words: This combination behaves for any destination type T
and any argument type U like a single constructor that is either
explicit or non-explicit (or no constructor at all).
As Praetorian points out this is exactly how libstdc++ implements it.
If we modify the OPs example accordingly, it also works:
struct S {
template <typename T,
typename std::enable_if< std::is_integral<T>::value, bool>::type = false>
S(T) {}
template <typename T,
typename std::enable_if<!std::is_integral<T>::value, bool>::type = false>
explicit S(T) {}
};
One way that seems to work with most compilers is to add a dummy parameter to one of the functions, to make them slightly different.
// constructor is explicit if T is integral
struct S {
template <typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
S(T t) {}
template <typename T,
typename = typename std::enable_if<!std::is_integral<T>::value>::type,
typename dummy = void>
explicit S(T t) {}
};
int main()
{
S s1(7);
S s2("Hello");
}
Compiles with MSVC 2015.
I'm trying to write 2 template partial specializations which should take care of serializing sequence containers and associative containers differently but it seems like that the sequence container specialization is not used at all since the compiler tries to specialize it with the wrong specialization, here's the code:
template<typename W, typename T, template<typename...> class C, typename... Args>
class Archiver<W, C<T, Args...>>
{
public:
template<typename U = C<T, Args...>, typename std::enable_if<std::is_same<typename U::value_type, T>::value, int>::type = 0>
static void serialize(const W& w, const C<T, Args...>& container)
{
...
}
template<typename U = T, typename std::enable_if<!std::is_base_of<archive::require_placement_move, U>::value, int>::type = 0,
typename J = C<T,Args...>, typename std::enable_if<std::is_same<typename J::value_type, T>::value, int>::type = 0>
static void unserialize(const W& r, const Context& c, C<T,Args...>& container)
{
...
}
template<typename U = T, typename std::enable_if<std::is_base_of<archive::require_placement_move, U>::value, int>::type = 0,
typename J = C<T,Args...>, typename std::enable_if<std::is_same<typename J::value_type, T>::value, int>::type = 0>
static void unserialize(const W& r, const Context& c, C<T,Args...>& container)
{
...
}
};
template< typename W, typename K, typename T, template<typename...> class M, typename... Args>
class Archiver<W, M<K, T, Args...>>
{
public:
template<class U = M<K,T,Args...>, typename std::enable_if<std::is_same<typename U::key_type, K>::value && std::is_same<typename U::mapped_type, T>::value, int>::type = 0>
static void serialize(const W& w, const M<K,T,Args...>& container)
{
...
}
template<class U = M<K,T,Args...>, typename std::enable_if<std::is_same<typename U::key_type, K>::value && std::is_same<typename U::mapped_type, T>::value, int>::type = 0>
static void unserialize(const W& r, const Context& c, M<K,T,Args...>& container)
{
...
}
};
If I try to use
Writer w;
Archiver<Writer, std::list<float>>::serialize(...);
the resolution is done on the second specialization, thus leading to the fact that
Candidate template ignored: substitution failure [with U = std::__1::list<float, std::__1::allocator<float> >]: no type named 'key_type' in 'std::__1::list<float, std::__1::allocator<float> >'
Which should imply that the first specialization is excluded a priori, like if the deduction resolution is choosing the one related to the map. Or maybe I'm missing something really stupid since I'm working on this code since some time.
I think your use of SFINAE is a bit off.
You are explicitly specifying the container type in the Archive template. Some unknown C++ rule means that the second specialization is chosen when you pass std::list<float>. SFINAE is then applied to the serialize function, eliminating the only available overload (hence the error message). Because the second specialization of Archive has been chosen, the serialize function in the first specialization is never even considered. This is not what you want.
Since you are explicitly specifying the container type, I am unsure why you are trying to use SFINAE at all. To properly specialize separately for sequence and associative containers, you will probably have to wait until concepts get standardized. Until then, you can do something naive, like specialize based on the presence of the type alias key_type (which is kind of what you were already doing).
Here is an example:
#include <type_traits>
template<typename>
using void_t = void;
template<typename T, typename = void>
struct has_key_type : std::false_type
{
};
template<typename T>
struct has_key_type<T, void_t<typename T::key_type>> : std::true_type
{
};
template<typename C, typename = std::enable_if_t<has_key_type<C>::value>>
void serialize(C const& c)
{
};
template<typename C, typename = std::enable_if_t<!has_key_type<C>::value>, typename = void>
void serialize(C const& c)
{
};
#include <iostream>
#include <list>
#include <map>
int main()
{
serialize(std::list<int>());
serialize(std::map<float, float>());
}
Since there is a restriction on allowed non-type variadic templates, I am trying to write a function taking an arbitrary number of doubles using enable_if. In essence, I want to do something like:
template<typename... T,
typename = typename std::enable_if<std::is_convertible<T, double>::value, T>::type>
foo(T... t){ /* code here */ }
I'm opting to put the enable_if as a default value for an unnamed parameter since my function is actually a constructor and will not have a return value. This would work for a single parameter, but as it's a variadic template T is a parameter pack, and the above code is not valid. So, how can I check every parameter is convertible to a double?
The bool_pack trick again.
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
Then
template<class R, class... Ts>
using are_all_convertible = all_true<std::is_convertible<Ts, R>::value...>;
and finally
template<typename... T,
typename = typename enable_if<are_all_convertible<double, T...>::value>::type>
foo(T... t){ /* code here */}
You could use fold expression in c++17 to do the same thing as other answers posted here but without the hassle of creating templates.
#include <type_traits>
template <typename... T, typename =
typename std::enable_if<
(true && ... && std::is_convertible_v<T, ___YOUR_TYPE___>),
void
>::type
>
constexpr auto foo(T...) noexcept {
// your code
}
And if you have access to C++20, you can use concepts:
#include <type_traits>
template <typename... T>
requires(
(... && std::is_convertible_v<T, ___YOUR_TYPE___>)
)
constexpr auto foo(T...) noexcept {
// your code
}
I think the simpler would be to use std::initializer_list:
foo(std::initializer_list<double> args)
{
// Your stuff.
}
instead of variadic template.
It may require to use {} instead of/ in addition to ()
Here is another (c++11) version (heavily inspired by the T.C.'s one above):
#include <type_traits>
template <typename To, typename From, typename... R>
struct are_all_convertible {
constexpr static bool value = std::is_convertible<From,To>::value &&
are_all_convertible<To,R...>::value;
};
template <typename To, typename From>
struct are_all_convertible<To,From> {
constexpr static bool value = std::is_convertible<From,To>::value;
};
template<typename... T,
typename = typename std::enable_if<are_all_convertible<double, T...>::value>::type>
foo(T... t){ /* code here */}
Here is a generic approach – a TMP for binary folding, using C++14. First, let's define the basic combining operations:
#include <type_traits>
struct and_op
{
using type = bool;
using identity = std::true_type;
template <bool A, bool B> static constexpr bool value = A && B;
};
struct or_op
{
using type = bool;
using identity = std::false_type;
template <bool A, bool B> static constexpr bool value = A || B;
};
Now the actual fold mechanic:
template <typename Op, typename Op::type...>
struct fold;
template <typename Op>
struct fold<Op> : Op::identity {};
template <typename Op, typename Op::type Val>
struct fold<Op, Val>
: std::integral_constant<typename Op::type
, Val> {};
template <typename Op, typename Op::type Val, typename Op::type... Tail>
struct fold<Op, Val, Tail...>
: std::integral_constant<typename Op::type
, Op::template value<Val, fold<Op, Tail...>::value>> {};
Next, we need a way to create unary traits from binary traits by binding:
template <template <typename, typename> class BPred, typename T>
struct bind_pred
{
template <typename U>
struct pred_1st : std::integral_constant<bool, BPred<T, U>::value> {};
template <typename U>
struct pred_2nd : std::integral_constant<bool, BPred<U, T>::value> {};
};
Finally, a helper wrapper to combine the result of applying a unary predicate:
template <typename Op, template <typename> class UPred, typename ...Args>
struct fold_pred : fold<Op, UPred<Args>::value...> {};
That's it. Now let's get to work:
template <typename T>
using maybe_double = bind_pred<std::is_convertible, double>::pred_2nd<T>;
#include <iomanip>
#include <iostream>
int main()
{
std::cout
<< std::boolalpha
<< fold_pred<and_op, maybe_double, int, float>::value << '\n'
<< fold_pred<and_op, maybe_double, int, float, void>::value << '\n';
}
In C++17 (or C++1z, rather), you can write direct solutions with less code thanks to the new fold expressions. For example:
template <template <typename> class UPred, typename ...Args>
static constexpr bool pred_all = (UPred<Args>::value && ...);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ unary fold
Usage:
static_assert(pred_all<maybe_double, int, float>);
I'm trying to switch between an explicit and an implicit conversion constructor via enable_if.
My code currently looks like
#include <type_traits>
#include <cstdint>
enum class enabled {};
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T = void> using disable_if_t = typename std::enable_if<!B, T>::type;
template <std::intmax_t A> struct SStruct
{
static constexpr std::intmax_t a = A;
};
template <typename T> struct SCheckEnable : std::integral_constant<bool, T::a == 0>
{
};
template <typename U, typename T> class CClass
{
public:
template <typename T2, enable_if_t<SCheckEnable<U>::value, enabled>...> constexpr CClass(T2 v) : val(v) {};
template <typename T2, disable_if_t<SCheckEnable<U>::value, enabled>...> explicit constexpr CClass(T2 v) : val(v) {};
private:
T val;
};
int main()
{
CClass<SStruct<0>, double> a = 1; // should use implicit constructor
CClass<SStruct<1>, double> b = CClass<SStruct<1>, double>(1); // should use explicit constructor
}
The true in the enable_ifs is dependent of the template parameter U
If I try to compile this minimal example with g++ 4.9.1 and --std=c++11 enabled I get the following errors
sfinae.cpp: In substitution of ‘template<bool B, class T> using disable_if_t = typename std::enable_if<(! B), T>::type [with bool B = true; T = enabled]’:
sfinae.cpp:13:52: required from here
sfinae.cpp:7:95: error: no type named ‘type’ in ‘struct std::enable_if<false, enabled>’
template <bool B, typename T = void> using disable_if_t = typename std::enable_if<!B, T>::type;
^
sfinae.cpp:19:68: error: prototype for ‘constexpr CClass<U, T>::CClass(T2)’ does not match any in class ‘CClass<U, T>’
template <typename U, typename T> template <typename T2> constexpr CClass<U, T>::CClass(T2 v) : val(v)
^
sfinae.cpp:13:77: error: candidates are: template<class U, class T> template<class T2, int ...<anonymous> > constexpr CClass<U, T>::CClass(T2)
template <typename T2, disable_if_t<true, enabled>...> explicit constexpr CClass(T2 v);
^
sfinae.cpp:12:67: error: template<class U, class T> template<class T2, enabled ...<anonymous> > constexpr CClass<U, T>::CClass(T2)
template <typename T2, enable_if_t<true, enabled>...> constexpr CClass(T2 v);
^
Any idea how to select between explicit and implicit construction based on parameter U here?
Use
template <class...> struct null_v : std::integral_constant<int, 0> {};
and define the constructors as
template <typename T2,
long = null_v<enable_if_t<SCheckEnable<U>::value, T2>>::value>
constexpr CClass(T2 v) : val(v) {};
template <typename T2,
int = null_v<disable_if_t<SCheckEnable<U>::value, T2>>::value>
explicit constexpr CClass(T2 v) : val(v) {};
Making the argument dependent and actually instantiated.
Demo.
[temp.deduct]/8:
If a substitution results in an invalid type or expression, type
deduction fails. An invalid type or expression is one that would be
ill-formed if written using the substituted arguments.
In your case the error occurs outside of any substitution, so that's not causing a deduction failure but rather makes your code ill-formed.