When the expression inside decltype includes a function template, the compiler only looks at the signature of the template function to determine if it is possible to instantiate the template if the expression was really in the evaluated context. The actual function definition is not used at this point.
(This is actually why std::declval can be used inside decltype , although std::declval has no definition at all.)
The template constructor has the same signature as just declared, but not yet defined:
template <typename ...Args> Derived(Args&... args);
When decltype compiler simply looks at this information and decides that Derived{} is a valid expression, an rvalue of type Derived<> . Part : Base{args...} is part of the template definition and is not used inside decltype .
If you need a compiler error, you can use something like this to make your constructor more "SFINAE-friendly", which means that information about whether the template specialization is really valid falls into the signature of the template.
template <typename ... Args, typename Enable = std::enable_if_t<std::is_constructible<Base, Args&...>::value>> Derived( Args& ... args ) : Base{ args... } {}
You can also change the constructor to avoid "too perfect forwarding." If you are running Derived x; Derived y{x}; Derived x; Derived y{x}; , specialization of the Derived(Derived&); template Derived(Derived&); will be better than implicit Derived(const Derived&); , and you end up trying to pass x to Base{x} instead of using the implicit Derived copy constructor.
aschepler
source share