Suppose types can have Foo
, Bar
, Baz
methods, and we have type traits to check it. Eg for Foo
we have HasFoo
:
template <class Type>
constexpr bool DetectFoo (decltype (std::declval<Type> ().Foo ())*) { return true; }
template <class Type>
constexpr bool DetectFoo (...) { return false; }
template <class Type>
constexpr bool HasFoo = DetectFoo<Type> (nullptr);
Let's write a Wrapper<Type>
class template, that forwards the property of Type
having or not having these methods. For any Type
the following should be satisfied:
Wrapper<Type> w;
should compile, since we did not call the methods yet.wX ();
should compile iff HasX<Type>
, for X
= Foo
, Bar
, Baz
.HasX<Wrapper<Type>> == HasX<Type>
should hold, for X
= Foo
, Bar
, Baz
. To conditionally enable a method in Wrapper
there is a clear way in C++20:
template <class Type>
struct Wrapper {
void Foo ()
requires HasFoo<Type>;
};
But what to do in earlier C++ standards, without concepts? I have come up with the following idea (see live demo ):
template <class Type>
struct Wrapper {
template <bool dummy = true, class = std::enable_if_t<HasFoo<Type> && dummy>>
void Foo ();
};
This answer says it is ill-formed, NDR, but I don't understand the explanation. Is this really ill-formed, NDR?
Your code is well-formed.
The linked answer refers to [temp.res.general]/8.1
:
The validity of a template may be checked prior to any instantiation. ... The program is ill-formed, no diagnostic required, if:
— no valid specialization can be generated for a template ... and the template is not instantiated, ...
Here, the "template" that we're talking about is Foo
.
I believe this can be interpreted in two ways:
(1) We can consider Wrapper<A>::Foo
and Wrapper<B>::Foo
to be the same template (for every A
, B
). Then, the existence of such a template argument for Wrapper
that makes the condition in enable_if_t
true
is alone enough to make the code well-formed.
(2) We can also consider Wrapper<A>::Foo
and Wrapper<B>::Foo
to be different templates (for A != B
). Then, if there existed such a template argument for Wrapper
for which it's impossible to instantiate Foo
, your code would be ill-formed NDR. But it never happens, because you can always specialize HasFoo
to be true
for every template argument!
In any case, I argue that (1) is the intended interpretation. The purpose of [temp.res.general]/8.1
is not to get in your way, but to help you by validating templates early when possible. I've never seen compilers use the second interpretation.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.