Using a template alias instead of a template within a template

前端 未结 2 435
故里飘歌
故里飘歌 2020-12-06 05:58

From a previous question:

Doing a static_assert that a template type is another template

Andy Prowl provided me with this code that allows me to static

相关标签:
2条回答
  • No, it cannot be solved (not without changing the design significantly at least). The problem is that template alias names are not deduced, as mentioned in paragraph 14.5.7/2 of the C++11 Standard:

    When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template. [ Note: An alias template name is never deduced.—end note ]

    The paragraph also provides an example:

    [ Example:

    template<class T> struct Alloc { / ... / };
    template<class T> using Vec = vector<T, Alloc<T>>;
    Vec<int> v; // same as vector<int, Alloc<int>> v;
    
    ...
    
    template<template<class> class TT>
    void f(TT<int>);
    f(v); // error: Vec not deduced                          <=== Relevant
    
    ...
    

    end example ]

    In your concrete case, the problem is that when trying to match the partial specialization, the compiler won't deduce that your type is an instantiation of foo_alt (since foo_alt is the name of an alias template), and the primary template gets picked.

    If you want to use alias templates, you will have to give up a bit of genericity and create a type trait specific for foo:

    #include <type_traits>
    
    template<typename T>
    struct foo {};
    
    template<typename T>
    struct is_instantiation_of_foo : std::false_type { };
    
    template<typename...Ts>
    struct is_instantiation_of_foo<foo<Ts...>> : std::true_type { };
    

    Which you could then use this way:

    template<typename FooType>
    struct bar {
      static_assert(is_instantiation_of_foo<FooType>::value, ""); //fail
    };
    

    Now, none of the assertions in the following program will fire:

    template<typename T>
    using foo_alt = foo<T>;
    
    int main(int,char**) {
      // None of these fail:
      bar<foo<int>> b;
      bar<foo_alt<int>> b2;
    
      return 0;
    }
    

    Here is a live example.

    0 讨论(0)
  • 2020-12-06 06:07

    If you have an alias template that does not alter the template parameters of the referred class (like in your example; it just renames the referred template), then you can use something like (it's not the most elegant way)

    template < template<class...> class TT0, template<class...> class TT1,
               class... Ts1 >
    struct is_from_same_template_helper
    {
        template < class T = TT0<Ts1...>,
                   class = typename std::enable_if<
                       std::is_same<TT0<Ts1...>, TT1<Ts1...>>::value
                   >::type
                 >
        static std::true_type test(int);
    
        template < class T = int >
        static std::false_type test(...);
    };
    
    
    template<template<class...> class, class>
    struct is_instantiation_of : public std::false_type { };
    
    template<template<class...> class TT0, template<class...> class TT1,
             class... Ts1>
    struct is_instantiation_of<TT0, TT1<Ts1...>>
        : public decltype( is_from_same_template_helper<TT0, TT1, Ts1...>
                           ::template test<>(0) )
    { };
    

    Thanks to Yakk for pointing out that you need a SFINAE-type of check in order to only let the static_assert fail (previous version failed due to a compiler error before the static_assert).

    As only the specializations of alias templates are equal to specializations of the referred template; the alias template itself is not equal to the referred template.

    If the alias template does alter the template parameters, the implementation above can create false negatives.


    As an xy-Problem, you could change your implementation to:

    #include <type_traits>
    
    
    template < template <typename...> class TT0, template <typename...> class TT1 >
    struct is_same_template : public std::false_type { };
    
    template < template <typename...> class TT >
    struct is_same_template < TT, TT > : public std::true_type { };
    
    
    template < typename T0, typename T1 >
    struct is_from_same_template : public std::false_type { };
    
    template < template <typename...> class TT0, template <typename...> class TT1,
               typename... Ts0, typename... Ts1 >
    struct is_from_same_template < TT0<Ts0...>, TT1<Ts1...> >
        : public is_same_template<TT0, TT1> { };
    
    
    template<typename T>
    struct foo {};
    
    //Added: alias for foo
    template<typename T>
    using foo_alt = foo<T>;
    
    template<typename FooType>
    struct bar {
      //Changed: want to use foo_alt instead of foo here
      static_assert(is_from_same_template<foo_alt<int>, FooType>::value, "");
    };
    
    int main(int,char**) {
      //both of these succeed:
      bar<foo<int>> b;
      bar<foo_alt<int>> b2;
    
      return 0;
    }
    

    Of course, you need to have a valid instantiation of foo_alt for this approach.

    0 讨论(0)
提交回复
热议问题