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
Related
I needed a type trait that decays enums to their underlying type, and works the same as decay_t for all other types. I've written the following code, and apparently this is not how SFINAE works. But it is how I thought it should work, so what exactly is the problem with this code and what's the gap in my understanding of C++?
namespace detail {
template <typename T, std::enable_if_t<!std::is_enum_v<T>>* = nullptr>
struct BaseType {
using Type = std::decay_t<T>;
};
template <typename T, std::enable_if_t<std::is_enum_v<T>>* = nullptr>
struct BaseType {
using Type = std::underlying_type_t<T>;
};
}
template <class T>
using base_type_t = typename detail::BaseType<T>::Type;
The error in MSVC is completely unintelligible:
'detail::BaseType': template parameter '__formal' is incompatible with
the declaration
In GCC it's a bit better - says that declarations of the second template parameter are incompatible between the two BaseType templates. But according to my understanding of SFINAE, only one should be visible at all for any given T and the other one should be malformed thanks to enable_if.
Godbolt link
SFINAE applied to class templates is primarily about choosing between partial specialisations of the template. The problem in your snippet is that there are no partial specialisations in sight. You define two primary class templates, with the same template name. That's a redefinition error.
To make it work, we should restructure the relationship between the trait implementations in such as way that they specialise the same template.
namespace detail {
template <typename T, typename = void> // Non specialised case
struct BaseType {
using Type = std::decay_t<T>;
};
template <typename T>
struct BaseType<T, std::enable_if_t<std::is_enum_v<T>>> {
using Type = std::underlying_type_t<T>;
};
}
template <class T>
using base_type_t = typename detail::BaseType<T>::Type;
The specialisation provides a void type for the second argument (just like the primary would be instantiated with). But because it does so in a "special way", partial ordering considers it more specialised. When substitution fails (we don't pass an enum), the primary becomes the fallback.
You can provide as many such specialisation as you want, so long as the second template argument is always void, and all specialisations have mutually exclusive conditions.
BaseType isn't being partial-specialized, you're just redeclaring it, and since the non-type parameter has a different type, the compilation fails. you might want to do
#include <type_traits>
namespace detail {
template <typename T, bool = std::is_enum_v<T>>
struct BaseType;
template <typename T>
struct BaseType<T, false> {
using Type = std::decay_t<T>;
};
template <typename T>
struct BaseType<T, true> {
using Type = std::underlying_type_t<T>;
};
}
You declare the same struct with different parameter, which is forbidden.
You can do it with partial specialization:
namespace detail {
template <typename T, typename Enabler = void>
struct BaseType {
using Type = std::decay_t<T>;
};
template <typename E>
struct BaseType<E, std::enable_if_t<std::is_enum_v<E>>>
{
using Type = std::underlying_type_t<E>;
};
}
Demo
First of all I'm very sorry for the question title, but it's very hard to describe.
What of those two below is valid syntax if I want to specialized Resolve for all instantiation of A?
1)
template<uint32_t I> struct A {};
template<typename> struct Resolve;
template<uint32_t I>
struct Resolve<A<I>>
{
void f() { printf("im here!\n"); }
};
2)
template<uint32_t I> struct A {};
template<typename> struct Resolve;
template<>
template<uint32_t I>
struct Resolve<A<I>>
{
void f() { printf("im here!\n"); }
};
Or is template<> optional? There's two different answers on SO: here and here.
Also please provide quotation of the standard if possible.
Option 2) doesn't compile on MSVC, but does compile at least on some versions of GCC.
This is correct:
template <uint32_t I>
struct Resolve<A<I>>
{ };
The syntax template <> is used to introduce an explicit specialization (of a class template, function template, whatever) (see [temp.spec]/3 and [temp.expl.spec]/1). But we're trying to do a partial specialization. A partial specialization still needs to introduce template parameters, an explicit specialization does not.
On the other hand, if we were trying to specialize a member of an explicit specialization, then we'd use template <>. For instance:
template <class T>
struct A {
template <class T2> struct B { }; // #1
};
template <> // for A
template <class T2> // for B
struct A<int>::B<T2> { }; // #2
A<char>::B<int> x; // #1
A<int>::B<char> y; // #2
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>
{};
I have found that the following piece of code:
#include <iostream>
#include <vector>
template <typename T>
struct X : std::false_type {};
template <template <typename> class Y, typename U>
struct X<Y<U>> : std::true_type {};
int main() {
if (X<int>())
std::cout << "wrong\n";
if (X<std::vector<double>>())
std::cout << "correct\n";
return 0;
}
Only prints correct when compiled with g++-7 with -std=c++1z. Other versions of g++, clang++ or other std flags fail to produce correct.
Is this a bug of the current implementation, and this code should not print anything, or is something changed in C++17 which makes this code work as I expect?
This is a result of the adoption of P0522 in C++17, whose motivation was, from the example:
template <template <int> class> void FI();
template <template <auto> class> void FA();
template <auto> struct SA { /* ... */ };
template <int> struct SI { /* ... */ };
FI<SA>(); // OK; error before this paper
FA<SI>(); // error
template <template <typename> class> void FD();
template <typename, typename = int> struct SD { /* ... */ };
FD<SD>(); // OK; error before this paper (CWG 150)
Previously, the wording in [temp.arg.template] required template template parameters to match in kind exactly. So given:
template <template <class > class P> class X;
template <class T> class A;
template <class T, class U=T> class B;
template <class... > class C;
X<A> is clearly okay, but X<B> is ill-formed because B takes two template parameters (regardless if one is defaulted!) and X<C> is ill-formed because P expects one template parameter and C takes a pack (even if you could form a C with only a single parameter!)
The new wording loosens the idea of a match to one which makes more sense - if the template template-parameter is at least as specialized as the argument. That makes X<B> and X<C> both work.
Hence, in C++17, X<std::vector<double>> should pick the specialization. But before C++17, it should pick the primary. gcc is doing the right thing.
The rules for picking which class template specialization is preferred involve rewriting the specializations into function templates and determining which function template is more specialized via the ordering rules for function templates [temp.class.order]. Consider this example, then:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <class T, class U> struct A { };
template <class T> int foo(A<T, void_t<T>> ) { return 1; }
template <class T> int foo(A<T*, void> ) { return 2; }
int main() {
std::cout << foo(A<int*, void>{});
}
Both gcc and clang print 2 here. This makes sense with some previous examples - deducing against a non-deduced context (void against void_t<T>) is just ignored, so deducing <T, void_t<T>> against <X*, void> succeeds but deducing <T*, void> against <Y, void_t<Y>> fails in both arguments. Fine.
Now consider this generalization:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <int I> struct int_ { static constexpr int value = I; };
template <class T, class U> struct A : int_<0> { };
template <class T> struct A<T, void_t<T>> : int_<1> { };
template <class T> struct A<T*, void> : int_<2> { };
int main() {
std::cout << A<int*, void>::value << '\n';
}
Both clang and gcc report this specialization as ambiguous, between 1 and 2. But why? The synthesized function templates aren't ambiguous. What's the difference between these two cases?
Clang is being GCC-compatible (and compatible with existing code that depends on both of these behaviors).
Consider [temp.deduct.type]p1:
[...] an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
The crux of the issue is what "compatible" means here.
When partially ordering function templates, Clang merely deduces in both directions; if deduction succeeds in one direction but not the other, it assumes that means the result will be "compatible", and uses that as the ordering result.
When partially ordering class template partial specializations, however, Clang interprets "compatible" as meaning "the same". Therefore it only considers one partial specialization to be more specialized than another if substituting the deduced arguments from one of them into the other would reproduce the original partial specialization.
Changing either of these two to match the other breaks substantial amounts of real code. :(