Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specialize template with function pointer, that depends on template parameter

I would like to have a template with a nested value which should be initialized by a given initializer function:

template <typename T, T(INIT)()> struct Foo
{
    T value = INIT();
};

It can be used this way:

// Some random type only instanceable through factory()
struct Bar
{
    int bar{};
private:
    // The only way to create a Bar is through factory()
    friend Bar factory();
    Bar() {};
};

Bar factory() { return {}; }

Foo<Bar, factory> foo;

But, if no function is provided, the template should try to default-initialize the nested value, so I've tried to specialize the template:

template <typename T> struct Foo<T, nullptr>
{
    T value{};
};

The idea is to use it this way:

struct Baz{};

Foo<Bar, factory> foo; // Nested Bar have Bar::bar initialized through factory function.
Foo<Baz>          baz; // No factory function needed, nested Baz default-initialized.

But I just discovered that template partial specialization types cannot rely on other template types, the error I'm getting is pasted below:

error: type 'T (*)()' of template argument 'nullptr' depends on a template parameter template struct Foo


Is there a way to achieve my goal? It would be nice if it works with template variables as well:

template <typename T, T(INIT)()> T Foo = INIT();
template <typename T>            T Foo<T, nullptr>{};

Extra question: Why partial specializations cannot depend on template parameters? What's the rationale behind this restriction?

like image 281
PaperBirdMaster Avatar asked May 29 '15 11:05

PaperBirdMaster


People also ask

When we specialize a function template it is called?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

What is the specialty of a template function give example?

Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.

Can a template be a template parameter?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

What is a template template parameter in C++?

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.


3 Answers

For your case, you may use:

template <typename T>
T default_construct() { return T{}; }

template <typename T, T(INIT)() = &default_construct<T>>
struct Foo
{
    T value = INIT();
};

And then use it like:

Foo<int> f;
Foo<int, bar> b;

Live demo

like image 112
Jarod42 Avatar answered Oct 13 '22 08:10

Jarod42


If it's only about doing a default initialization if the second template parameter is missing, you may provide a templated default initialization function as the default parameter like.

template<typename T> 
    T do_default_assign() { 
        return T(); 
    };                                                                      

template <typename T, T (INIT)() = do_default_assign<T> > struct Foo 
    { 
        T value = INIT(); 
    };

This however suffers an unnecessary "return by value" and assignment operation which might be costly or impossible for some T.

like image 22
Oncaphillis Avatar answered Oct 13 '22 08:10

Oncaphillis


You can define a constructor template function that will initialize a value of type Type and then use it as a default constructor:

template<typename Type, typename... Args>
Type constructor(Args... args) { 
    return Type(std::forward<Args>(args)...);
}

and then use it as default template argument for the function:

template <typename T, T(INIT)() = constructor<T>> struct Foo
{
    T value = INIT();
};

Live demo

like image 27
Shoe Avatar answered Oct 13 '22 10:10

Shoe