Quuxplusone
Quuxplusone

Reputation: 27290

SFINAE to detect the explicitness of a CTAD deduction guide

Little-known feature of CTAD (class template argument deduction) in C++17: you can mark user-defined deduction guides as explicit. (Godbolt.)

template<class T> struct A { A(int); };           A(int) -> A<int>;
template<class T> struct B { B(int); };  explicit B(int) -> B<int>;

A<int> a = 1;  // OK, constructor of A<int> is implicit
B<int> b = 1;  // OK, constructor of B<int> is implicit

auto a = A(1);  // OK, deduction guide of A exists
auto b = B(1);  // OK, deduction guide of B exists

A a = 1;  // OK, deduction guide of A is non-explicit
B b = 1;  // ERROR!! deduction guide of B is explicit

So, class templates A and B have observably different behavior. I'd like to write a unit test that static_asserts that one of my templates behaves like B, not like A.

static_assert(!has_properly_explicit_deduction_guide_v<A>);
static_assert(has_properly_explicit_deduction_guide_v<B>);

Is this possible in C++17 and/or C++20 and/or "C++future"?

Upvotes: 2

Views: 387

Answers (2)

ecatmur
ecatmur

Reputation: 157414

Currently, per [dcl.type.class.deduct], a placeholder for a deduced class type can appear in:

  • the initializing declaration of a variable;
  • a new-expression;
  • explicit type conversion (functional notation); or
  • a non-type template parameter [NTTP].

The first of these is not SFINAE-able (either classically or in a requires clause), since it is a declaration and not an expression; the next two invoke direct-initialization, not copy-initialization; and the last only works for structural types with constexpr constructors (although this isn't particularly clear from the Standard).

wrt. future direction, back in 2018 Mike Spertus was angling for CTAD on function template arguments: Improving function templates with Class Template Argument Deduction, How to make Terse Notation soar with Class Template Argument Deduction. There hasn't been much movement on this since then, but perhaps it could be resurrected.

Upvotes: 0

Barry
Barry

Reputation: 303337

No.

You can't check declarations, so you can't check the validity of A a = 1; or B b = 1;

And class template argument deduction isn't valid in any other copy-initialization contexts. So while you can check "implicitly convertible" by seeing if the expression [](To){}(from) is valid, you cannot check "implicitly class-template-argument-deducible" by seeing if the expression [](A){}(1) is valid, since you cannot just write A there.

The only thing that you can check is whether A(1) is valid or not, but that is direct initialization, and so would not be able to validate explicitness.

Upvotes: 3

Related Questions