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 ifstd::is_convertible<const Ti&, Ti>::value
is false for at least onei
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) {}
Explicit use of the this() or super() keywords allows you to call a non-default constructor. To call a non-args default constructor or an overloaded constructor from within the same class, use the this() keyword. To call a non-default superclass constructor from a subclass, use the super() keyword.
In programming, implicit is often used to refer to something that's done for you by other code behind the scenes. Explicit is the manual approach to accomplishing the change you wish to have by writing out the instructions to be done explicitly.
implicit constructor is a term commonly used to talk about two different concepts in the language, the. implicitly declared constructor which is a default or copy constructor that will be declared for all user classes if no user defined constructor is provided (default) or no copy constructor is provided (copy).
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) {} };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With