3

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>
{};
mgoldman
  • 169
  • 11
  • In [this](https://godbolt.org/g/WQdj12) example, adding a new specialization for `bar>` matches the second example. I guess those are the rules, I'm not 100% sure. – Francisco Gallego Salido Jan 17 '18 at 04:42
  • 2
    It looks like [this](https://ideone.com/rM4t4b) is your minimal example. – n. m. could be an AI Jan 17 '18 at 04:48
  • I find it hard to believe that compiles without any #include files. – Jive Dadson Jan 17 '18 at 04:59
  • @JiveDadson He didn't post the entire code, I guess you should follow the link to the compiler explorer – Francisco Gallego Salido Jan 17 '18 at 05:01
  • @Fransisco - I guess mgoldman should post a minimal, complete, verifiable example. – Jive Dadson Jan 17 '18 at 05:03
  • Please remove the link from the question, and post complete code, including needed #include files. – Jive Dadson Jan 17 '18 at 05:08
  • Possible duplicate of [Const and non const template specialization](https://stackoverflow.com/questions/14926482/const-and-non-const-template-specialization) – Francisco Gallego Salido Jan 17 '18 at 05:12
  • @JiveDadson I have updated the example to contain a fully compile-able snippet. – mgoldman Jan 17 '18 at 06:14
  • @n.m. I dont believe that your linked example provides the correct characteristics. I would not expect that example to work since the template "base" is specialized on a non-const Q. The interesting aspects of this example are the template template parameter and the usage of SFINAE for constraining it. – mgoldman Jan 17 '18 at 06:17
  • I don't think you are right. You have something that has form `A` and you are passing a `const C`. It won't match, SFINAE or not. `A` being a template template parameter is irrelevant. – n. m. could be an AI Jan 17 '18 at 06:34
  • Anyway [here](https://ideone.com/wBjLaE) is an example modified to use a template template parameter. SFINAE still cannot make things match if they don't match on their own. – n. m. could be an AI Jan 17 '18 at 06:44
  • Why do you think `const foo` will match `FooType`? – xskxzr Jan 17 '18 at 06:57
  • @n.m. Just to make sure we're not miscommunicating, here is your example with a sfinae constrained "base" specialization so that it works for const and non-const Q: https://ideone.com/Y24EQp. Here is a similar specialization of base. However, it uses template template parameters and does not compile: https://ideone.com/SHBTI5 – mgoldman Jan 17 '18 at 06:58
  • In the Y24EQp example you match against `QType` and then use SFINAE to *additionally* constrain the removed-`const` parameter to match `Q`. This works. You can use SFINAE to make unwanted types fail substitution. In the SHBTI5 example you are trying to match against `QType`. A `const` type cannot match this, the substitution will fail, and you cannot use SFINAE to make it un-fail. – n. m. could be an AI Jan 17 '18 at 09:25

1 Answers1

1

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>
{};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • If my understanding is correct, then there is no way, barring type traits or dependent types, to deduce the template parameterization of FooType? – mgoldman Jan 17 '18 at 14:04
  • Not sure I understand what you mean, but you can implement `is_same_template` (but care with aliases) and `template_element>::type` (similar to `tuple_element`) or use partial specialization, as you tried with `bar`, but your way doesn't handle cv_qualifiers. – Jarod42 Jan 17 '18 at 14:35
  • Can you point me to the relevant language in the standard concerning this type of pattern matching for specialziations. – mgoldman Jan 18 '18 at 15:30