Home > Back-end >  Declaring a class template member that belongs to all specializations
Declaring a class template member that belongs to all specializations

Time:09-22

What I'm looking for is a way to say: This is the same for all specializations:

template <typename T>
struct Foo {
  using id_type = unsigned int; // this does not depend on T!
};

Foo::id_type theId; // Doesn't matter what the specialization is, id_type is always the same.

I want to access id_type without having to specify the specialization...

CodePudding user response:

You can't have exactly what you are asking for. Foo is not a class. Foo<T> is a class, for any T.

You could have a non-template base that holds id_type

struct FooBase {
  using id_type = unsigned int;
};

template <typename T>
struct Foo : FooBase{};

FooBase::id_type theId;

You could provide a default parameter for T

template <typename T = struct FooPlaceholder>
struct Foo {
  using id_type = unsigned int; // this does not depend on T!
};

Foo<>::id_type theId;

However nothing stops me from writing an explicit specialisation of Foo that lacks (or redefines) id_type.

template <> struct Foo<MyType> { };    
template <> struct Foo<MyOtherType> { int id_type = 42; };

CodePudding user response:

Instead of id_type being a class (alias declaration) property, you could make it a stand-alone trait on a template template parameter:

#include <type_traits>

// Helper: compare with std::is_same but for
//         template template parameter type arguments.
template <template <typename> typename, template <typename> typename>
struct is_same_primary_template : std::false_type {};

template <template <typename> typename TT>
struct is_same_primary_template<TT, TT> : std::true_type {};

template <template <typename> typename TT, template <typename> typename UU>
constexpr bool is_same_primary_template_v{
    is_same_primary_template<TT, UU>::value};

template <typename T> struct Foo {};

template <template <typename> typename, typename Enable = void> struct id_type;

template <template <typename> typename TT>
struct id_type<TT, std::enable_if_t<is_same_primary_template_v<TT, Foo>>> {
  using type = int;
};

// ...

template <template <typename> typename TT>
using id_type_t = typename id_type<TT>::type;

int main() { id_type_t<Foo> theId; }

This approach has one drawback, though. As the trait is specialized over specific types it couples these types with the implementation of the trait (as you want be very careful with the location of your specializations, w.r.t. the location of the primary template).

When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

CodePudding user response:

Just an extension based on Caleth's answer and the comment ensuing from that. You can kind of protect yourself from "bad" specializations of Foo like this:

#include <iostream>
#include <type_traits>

//-------------------------------------------------------------------------
// from Caleth

struct FooBase
{
    using id_type = unsigned int;
};

template <typename T>
struct Foo : FooBase 
{
};

FooBase::id_type theId{};

//-------------------------------------------------------------------------
// specialization bypassing FooBase 

template<>
struct Foo<char>
{
};

//-------------------------------------------------------------------------
// compile time check if someone mad a "bad" specialization, pre C  20

template<typename T>
void f(const Foo<T>& foo)
{
    static_assert(std::is_base_of_v<FooBase, Foo<T>>); 
}

//-------------------------------------------------------------------------
// C  20 concept to check for FooBase

template<typename T>
concept HasFooBase = std::is_base_of_v<FooBase, T>;

// only accepts types derived from FooBase
void g(const HasFooBase auto& foo)
{
}

//-------------------------------------------------------------------------

int main()
{
    Foo<int> foo;
    Foo<char> bar;

    f(foo);
    g(foo);

    f(bar); // won't compile, error C2607: static assertion failed
    g(bar); // won't compile, error C7602: 'g': the associated constraints are not satisfied

    return 0;
}
  • Related