In answering this question about trying to construct a variadic forwarding reference constructor that should only be called if no other constructor is valid. That is, if there was a:
C(const char*, size_t) { } // 1
template <typename... T, ???> C(T&&... ) { } // 2
We'd want C c1{"abc", 2};
to call (1), despite the required conversion, but C c2{1, 2, 3};
to call (2), as (1) cannot apply.
I proposed the following solution:
template <typename... T,
typename = std::enable_if_t<!std::is_constructible<C, T&&...>::value>
>
C(T&&... ) { }
And by proposed, I mean, I tried it and was surprised to discover that it actually works. It compiles and does exactly what I had hoped for on both gcc and clang. However, I am at a loss to explain why it works or even if it's actually supposed to work and gcc and clang are both just being particularly accommodating. Is it? Why?
They are used to initialize member objects. If default values are supplied, the trailing arguments can be omitted in the expression list of the constructor. Note that if a constructor has any arguments that do not have default values, it is not a default constructor.
A return statement in the body of a constructor cannot have a return value.
base a declares a variable a of type base and calls its default constructor (assuming it's not a builtin type). base a(); declares a function a that takes no parameters and returns type base .
A constructor that takes no parameters is called a parameterless constructor. Parameterless constructors are invoked whenever an object is instantiated by using the new operator and no arguments are provided to new .
The issue with your code is that we just instantiated is_constructible
in a context where it gets the answer wrong. Any kind of caching in the template code is likely to result in bugs -- try printing is_constructible
on the same parameters after you call the constructor! It is likely to get it wrong.
Live example of how it can go wrong. Notice it claims C
cannot be constructed from an int&
, despite having done so on the previous line.
struct C {
C(const char*, size_t) {}
template <class... Ts,
typename = std::enable_if_t<!std::is_constructible<C, Ts&&...>::value>
>
C(Ts&&... ) { }
};
int main() {
int a = 0;
C x{a};
std::cout << std::is_constructible<C, int&>{} << '\n';
}
oops.
I suspect this might be an ODR violation -- the two definitions of is_constructible
have different types at different spots? Or maybe not.
Solution to the original problem that does not have this issue also posted.
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