Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor conditionally marked explicit

Tags:

c++

c++17

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) {} 
like image 768
Ryan Haining Avatar asked Oct 07 '15 17:10

Ryan Haining


People also ask

How do you call an explicit constructor?

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.

What is explicit and implicit call in C++?

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.

What is implicit constructor in C++?

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).


1 Answers

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) {} }; 
like image 56
Shafik Yaghmour Avatar answered Oct 05 '22 23:10

Shafik Yaghmour