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?
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.
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.
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.)
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.
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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With