Reputation: 1468
auto
is not allowed")I'm working on a generic C++ framework made up of several class templates that allow to combine specializations of each other. For external reasons, the language version is restricted to C++14. Using my framework in non-generic user code ends up in complex template specializations like
MyType<foo<bar, baz, quuz<quz>, zip<zap<zoo>>> myObj{
foo<bar, baz, quuz<quz>, zip<zap<zoo>>{
bar{},
baz{},
quuz<quz>{
quz{}
},
zap<zoo>{
zoo{}
}
}
};
Of course, template parameter types do not have to equal the types of ctor arguments, but in the case of my present code, this happens quite often. The displayed code is only a caricature of the real code (many more types/templates), but it should be sufficient to point out my problem (Please let me know if you need some compilable example code in order to evaluate):
When I cite all the template arguments needed, this causes a lot of noise in the code,
especially for those types that have to be named redundantly.
Type aliases (using
) do not look like the perfect remedy because the example would need
lots of these, as well.
I inspected the possibilities of template argument deduction, and
I learned that C++14 supports template argument deduction only with function templates
(such as the often-discussed example of std::make_pair
), but that
class template argument deduction (CTAD)
will not be available before C++17.
Using function-based template-argument deduction (and "make functions"), I can "simplify" the object initialization expression on the RHS of the example code above, which leads to something like
MyType<foo<bar, baz, quuz<quz>, zip<zap<zoo>>> myObj = make_obj(
make_foo(
bar{},
baz{},
make_quuz(quz{}),
make_zap(zoo{})
)
);
In contexts where auto
can be used, I can even reach my goal of brief & expressive code like
auto myObj = make_obj(make_foo(bar{}, baz{}, make_quuz(quz{}), make_zap(zoo{})));
but this seems to be possible only in certain contexts where, e.g., myObj
is a local variable.
I would like to realize a framework library that enables users to create their own, non-generic classes based on my templates, but in a way where they do not have to repeat (and remember) all the types (= template specializations) used to realize their class implementation, like here:
class UserType
{
public:
UserType() : m_frameworkImplementation{make_foo(/*...*/)} {}
private:
// This is illegal code, of course. Only to show what I'd like to realize...
auto m_frameworkImplementation;
};
Sadly, I'm not able to do anything with auto
here, and I cannot replace auto
with MyType
if I don't copy all the template arguments to it.
I also made some futile attempts with decltype
, lambdas and other stuff, but I didn't even gain
a hint on a possible solutions (and it feels to embarrassing to show any of these failed attempts here).
There doesn't seem to be a way to deduce the specialized type of a class template construct, or of
the return value of the deduced specialization of a function template (e.g., "make function"),
at a point where the deduced type is needed to instantiate the type of a member variable.
What did I overlook? Do I have to wait until I can use C++17 or C++20, to achieve the desired result?
Upvotes: 1
Views: 121
Reputation: 18090
CTAD is not going to help you with this, as the compiler needs to know the type of the members at the line it is parsing the member, unless the class is a template, and UserType
is not a template.
if the class must not be templated then you can use decltype
with "in class initialization of members" to create a simpler interface for members as follows.
#include <utility>
auto make_foo()
{
return (int)5;
}
#define DEFINE_MEMBER(var_name, initializer) \
decltype(initializer) var_name = initializer
class UserType
{
public:
UserType() {}
private:
DEFINE_MEMBER(m_frameworkImplementation, make_foo());
};
int main()
{
UserType type;
}
this compiles under -std=c++14
you only need to provide a function that returns the "default construtor", you don't need to do it for other constructors.
#include <utility>
int make_foo()
{
return {};
}
#define DEFINE_MEMBER(var_name, initializer) \
decltype(initializer) var_name = initializer
class UserType
{
public:
UserType(double val): m_frameworkImplementation{val} {}
private:
DEFINE_MEMBER(m_frameworkImplementation, make_foo());
};
int main()
{
UserType type(1);
}
Upvotes: 1