I am attempting to specialize a class on a type s.t. it ignores the constness of the given type. In this case, the type is a template template parameter:
template <class T, typename Enable = void>
struct bar
{
bar()
{
static_assert(!std::is_same<T,T>::value, "no implementation of bar for type T");
}
};
template <template <typename> class FooType, class T>
struct bar<FooType<T>, typename std::enable_if<std::is_same<typename std::remove_cv<FooType<T>>::type, foo<T>>::value>::type>
{};
The above code complains in both GCC 4.8.4 and clang 5.0 (with -std=c++11) that bar is undefined when used with a class matching the template parameterization of FooType. Even if I remove the sfinae parameter, the specialization is still unable to be found.
An example of this issue can be found here: https://godbolt.org/g/Cjci9C.
In the above example, the specialization's constructor has a static assert that goes unfound when used with a const FooType, even when the sfinae parameter is hard-coded to void. When used with a non-const FooType all works as expected.
Can someone please provide an explanation as to why the constness prohibits type deduction (matching?) in this context.
EDIT (Updated Code):
Here is a fully compile-able snippet. I tried to capture the minimum example in this snippet. The original link has been updated to reflect this example.
#include <assert.h>
#include <type_traits>
template <class T>
struct foo {};
template <class T, typename Enable = void>
struct bar
{
bar()
{
static_assert(!std::is_same<T,T>::value, "no implementation of bar for type T");
}
};
template <template <typename> class FooType, class T>
struct bar<FooType<T>, typename std::enable_if<std::is_same<typename std::remove_cv<FooType<T>>::type, foo<T>>::value>::type>
{};
int main()
{
bar<foo<int>> mut_foo; // This is fine.
// bar<const foo<int>> const_foo; // Error. No implementation found.
}
Removing the comment on the 2nd line of main triggers the static assert. I have also tried std::decay, and std::remove_const with no success.
EDIT (Non-duplicate Justification):
While the linked problem does ask a similar problem, it does not require the use of template template parameters. It also only provides a technique for solving the problem and does not justify why the given code snippet does not work. Interestingly that technique does not seem to work with template template parameters, since substituting the following snippet into the above example results in the same error:
template <template <typename> class FooType, class T>
struct bar<FooType<T>,
typename std::enable_if<std::is_same<FooType<T>, foo<T>>::value || std::is_same<FooType<T>, const foo<T>>::value>::type>
{};
const foo<int> doesn't match FooType<T>,
it would match const FooType<T> or T (or const T).
After specialization match, you might add SFINAE:
So, in your case, you may do
template <typename T> struct is_foo : std::false_type {};
template <typename T> struct is_foo<foo<T>> : std::true_type {};
template <class T>
struct bar<T,
typename std::enable_if<is_foo<typename std::remove_cv<T>::type>::value>::type>
{};
Related
Is it currently possible to constrain a class template that rejects type argument which is the specialization of the class template itself without using static_assert?
Since I cannot use requires expression to check if it is a valid typename, I have to create a class template instantiation validator that checks whether the class template passed is valid with template arguments:
template <template <typename...> typename Temp, typename... Ts>
requires requires { typename Temp<Ts...>; }
constexpr bool is_valid() { return true; }
template <template <typename...> typename, typename...>
constexpr bool is_valid() { return false; }
template <template <typename...> typename Temp, typename... Ts>
concept valid_instantiation = is_valid<Temp, Ts...>();
Since failed static_assert emits a hard error, just like this one:
template <typename>
class Hello;
template <typename>
inline constexpr bool is_hello_v = false;
template <typename T>
inline constexpr bool is_hello_v<Hello<T>> = true;
template <typename T>
class Hello {
static_assert(!is_hello_v<T>);
};
static_assert(valid_instantiation<Hello, int>);
static_assert(!valid_instantiation<Hello, Hello<int>>);
The second static assertion sure didn't compile unless I remove that ! that returns true which is not what I expected.
What I want to have is to silent the error and replace the static_assert, so that:
static_assert(valid_instantiation<Hello, int>);
static_assert(!valid_instantiation<Hello, Hello<int>>);
can be valid.
For the first static assertion, the Hello<int> instantiation is accepted just fine, while the second static assertion, Hello<Hello<int>> instantiation should be rejected because the template argument passed is the instantiation of the class template itself but I have no knowledge what constraints I'll be using to achieve these.
It's ok if it is impossible to do so, or otherwise.
Not sure it is what you want, but
template <typename T> struct is_Hello;
template <typename T> requires (!is_Hello<T>::value) class Hello;
template <typename T> struct is_Hello : std::false_type{};
template <typename T> struct is_Hello<Hello<T>> : std::true_type{};
template <typename T>
requires (!is_Hello<T>::value) // So Hello<Hello<T>> is not possible
class Hello
{
// ...
};
Not sure how you want to SFINAE or test it (the trait seems equivalent), as Hello<Hello<T>> cannot exist.
Demo
I have a struct
template <auto& t>
struct Foo {
using Type = decltype(t);
};
I also have a template class:
template <typename T> class MyClass {};
I want to create a specialization for this struct for any arg of type MyClass:
template <typename T>
struct Foo <MyClass<T>& t> {
using Type = int;
};
I'd Like to be able to use this class like:
Foo<true>::Type t = false;
This code doesn't compile. How can I do this kind of specialization? Is there some other approach using std::enable_if that I can use to accomplish this?
You can see the code at https://onlinegdb.com/1Qzum1Fs2J
Your code is near by the needed solution. The specialization simply needs a bit different syntax:
template <typename T> class MyClass {};
template < auto value >
struct Foo
{
void Check() { std::cout << "Default" << std::endl; }
};
template <typename T, MyClass<T> value>
struct Foo<value>
{
void Check() { std::cout << "Spezial" << std::endl; }
};
int main()
{
Foo<10> fi;
Foo<MyClass<int>{}> fm;
fi.Check();
fm.Check();
}
For gcc it needs trunk version. gcc 11 compiles but delivers wrong result!
See it working: Works on gcc trunk, clang trunk and msvc v19.30
As per C++20 and P0732R2 (Class Types in Non-Type Template Parameters) non-type template parameters may be of class type, allowing you to use partial specialization to specialize over non-type template arguments that are more specialized than the "any non-type template" argument of the primary template.
#include <type_traits>
// some class types
template<typename T> struct S{};
struct U {};
template<auto> // _any_ non-type template parameter
struct is_using_primary_template : std::true_type {};
// partial specialization for specialization of a class template type.
template<typename T, S<T> s>
struct is_using_primary_template<s> : std::false_type {};
// explicit specialization for some class type values.
constexpr S<void> sv{};
constexpr U u{};
template<>
struct is_using_primary_template<sv> : std::false_type {};
template<>
struct is_using_primary_template<u> : std::false_type {};
template<auto v>
constexpr bool is_using_primary_template_v{is_using_primary_template<v>::value};
static_assert(is_using_primary_template_v<0>);
static_assert(!is_using_primary_template_v<S<int>{}>); // partial spec.
static_assert(!is_using_primary_template_v<S<char>{}>); // partial spec.
static_assert(!is_using_primary_template_v<S<void>{}>); // explicit spec.
static_assert(!is_using_primary_template_v<sv>); // explicit spec.
static_assert(!is_using_primary_template_v<u>); // explicit spec.
Note that this is not any special case of partial specialization, but just typical use cases where the minimum requirement that all template parameters of each partial specialization are deducible is fulfilled.
That GCC 11 rejects this code is a bug (Bug 99699), which seems to have been (silently or via another bug report?) fixed in GCC 12/trunk. See the Q&A Partial specialization of templates over non-type literal parameters in C++20: clang and gcc disagree for details.
For how to apply this to your particular example, see #Klaus' answer
I have many EnableIf traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve trait that can be used to transform those into a boolean trait.
Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx
template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};
Now if you have an EnableIf trait like so
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
You can create a boolean version of that very quickly
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
Or the analogous variable template.
But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm
I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?
I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t, here's an interesting explanation
Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
live on compiler explorer
The reason why your approach will fail is that Predicate<T>> in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.
You can wrap your Predicate<T>> to a non-deduced context to make it work:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
Live Demo
Because inside a non-deduced context, the deduction won't happen for Predicate<T> part, instead, it will use the Predicate and T obtained from elsewhere.
As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t as a template alias, will be replaced by void in deduction phase (see [temp.alias]/2), thus no deduction will happen.
Here are some examples to illustrate it more clearly:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}
Say I have a simple template like this:
template<typename T>
class A {};
And I want to specify that the type-parameter T is of some unrelated type X<U> where U is not known (or unspecifyable).
Is there a way how to express that as a concept?
Is there a way how to express that as a concept?
You don't need a concept, class template specialization works just fine in your case.
As an example, you can do this:
template<typename T>
class A;
template<typename U>
class A<X<U>> { /* ... */ };
This way, unless A is instantiated with a type of the form X<U> (where U is unknown), you'll get a compile-time error because the primary template isn't defined. In other terms, it won't work for all the types but X<U> (for each U), where the latter matches the class template specialization that has a proper definition.
Note that I assumed X is a known type. That's not clear from your question.
Anyway, if it's not and you want to accept types of the form X<U> for each X and each U, you can still do this:
template<typename T>
class A;
template<template<typename> class X, typename U>
class A<X<U>> { /* ... */ };
As a minimal, working example:
template<typename>
struct S {};
template<typename>
class A;
template<typename U>
class A<S<U>> {};
int main() {
A<S<int>> aSInt;
A<S<double>> aSDouble;
// A<char> aChar;
}
Both A<S<int>> and A<S<double>> are fine and the example compiles. If you toggle the comment, it won't compile anymore for A<char> isn't defined at all.
As a side note, if you don't want to use class template specialization and you want to simulate concepts (remember that they are not part of the standard yet and they won't be at least until 2020), you can do something like this:
#include<type_traits>
template<typename>
struct X {};
template<typename>
struct is_xu: std::false_type {};
template<typename U>
struct is_xu<X<U>>: std::true_type {};
template<typename T>
struct A {
static_assert(is_xu<T>::value, "!");
// ...
};
int main() {
A<X<int>> aXInt;
A<X<double>> aXDouble;
// A<char> aChar;
}
That is, given a generic type T, static assert its actual type by means of another structure (is_xu in the example) that verifies if T is of the form X<U> (for each U) or not.
My two cents: the class template specialization is easier to read and understand at a glance.
template <typename T, template <typename> class C>
concept bool Template = requires (T t) { {t} -> C<auto>; };
Now given a class template:
template <typename T>
struct X {};
a type template parameter can be constrained using:
template <typename T> requires Template<T, X>
class A {};
or:
template <Template<X> T>
class A {};
DEMO
This will work also for types derived from X<U>.
I've been messing around with enable_if, and I seem to have stumbled upon some inconsistent behaviour. This is in VS2010. I've reduced it to the following sample.
#include <type_traits>
using namespace std;
// enable_if in initial template definition
template <class T, class Enable = enable_if<true>> struct foo {};
foo<int> a; //OK
// explicit specialisation
template <class T, class Enable = void> struct bar;
template <class T> struct bar<T, void> {};
bar<int> b; //OK
// enable_if based specialisation
template <class T, class Enable = void> struct baz;
template <class T> struct baz<T, std::enable_if<true>> {};
baz<int> c; //error C2079: 'c' uses undefined struct 'baz<T>'
Is this a bug in the code or the compiler?
std::enable_if<true> should be typename std::enable_if<true>::type.
std::enable_if<true> always names a type (as does std::enable_if<false>). In order to get substitution to fail when the condition is false you need to use the type nested typedef, which is only defined if the condition is true.
Your problem has very little to do with enable_if
// you declare a structure named baz which takes 2 template parameters, with void
// as the default value of the second one.
template <class T, class Enable = void> struct baz;
// Partial specialization for a baz with T and an std::enable_if<true>
template <class T> struct baz<T, std::enable_if<true>> {};
// Declare a baz<int, void> (remember the default parameter?):
baz<int> c; //error C2079: 'c' uses undefined struct 'baz<T>'
baz<int, void> has an incomplete type at that point. The same problem will occur without enable_if:
template <class T, class U = void>
struct S;
template <class T>
struct S<T, int>
{ };
S<double> s;
And, as James said, you're using enable_if incorrectly. Boost's documentation for enable_if does a great job explaining it.