Class template argument dependent on constructor - c++

With a templated number wrapping struct:
template <int I> struct Num { static const int n = I; };
and a few overloaded functions:
template <typename T>
Num<0> id(T x) { return Num<0>(); }
Num<1> id(int x) { return Num<1>(); }
Num<2> id(double x) { return Num<2>(); }
Num<3> id(char x) { return Num<3>(); }
I can initialise the m_i member of a Zod struct using decltype and the type of the return argument of id:
template <typename T>
struct Zod {
Zod(T x) { m_i = identity<decltype(id(x))>::type::n; }
int m_i;
};
However, what I'd really like is for the Zod struct to have a second integer template argument initialised to the value which m_i was set to.
template <typename T, int I = ?>
struct Zod { ... }
This seems possible, as the identity/decltype expression evaluates to a compile time constant; for example, this is fine at global scope:
char c;
static const int g = identity<decltype(id(c))>::type::n;
The problem is that the x argument of the constructor is not available in the scope of Zod's template declaration. Can it be done?

It's perfectly possible- just pass in *((T*)nullptr) to obtain an lvalue of any type T regardless of it's constructability. After all, all you actually do with the constructor argument is pass it to id and then decltype that, which is perfectly doable in the template, since you know that the type of x is T.
template<typename T, int I = identity<decltype(id(*((T*)nullptr)))>::type::n> struct Zod {
...
};

Related

How to define a class template with reference type template parameter of template template parameter type

I would like to define a class template (hereafter called C) which takes a reference to an object of an to-be instantiated class template (hereafter called S) as template parameter. The objective is that C can be fully instantiated with one template argument.
S is a class template on its own which has one integral type template parameter. The C class template shall be instantiated using a reference to an object of any instantiation of S.
This is what I am trying to achieve:
template<int I> struct S {
int get() { return 42 + I; }
};
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ my desperate attempt
template< typename S<int I>::template & n>
struct C {
int get() {
return n.get();
}
};
S<42> s;
int main()
{
C<s> c;
return c.get();
}
The compiler I am using supports GNU++11 or older.
In C++17, you might do
template<int I> struct S { int get() { return 42 + I; } };
template <auto& n>
struct C;
template <int I, S<I>& n>
struct C<n>
{
int get() { return n.get(); }
};
S<42> s;
int main()
{
C<s> c;
return c.get();
}
Demo.
Before C++17, template <auto& n> struct C; has to be replaced. for example by
template <typename T, T& n> struct C;
template <typename T, T& n>
struct C;
template <int I, S<I>& n>
struct C<S<I>, n>
{
int get() { return n.get(); }
};
S<42> s;
#define AUTO(x) decltype(x), x
int main()
{
C<S<42>, s> c;
// C<AUTO(s)> c;
return c.get();
}
Demo
There is no way that I know in C++11 that will allow you to change just the template parameters to do what you want. What you can do though is not have a non-type template parameter, but just a type, and add a constructor to C that takes the reference to the desired object as a parameter:
template<typename T>
struct C {
C(T &t): t(t) {}
int get() {
return t.get();
}
private:
T &t;
};
Then you could declare c as follows:
C<decltype(s)> c(s);
However, it is of course not so nice to have to repeat yourself like that, so the trick is to make a templated function that will construct a C of the right type for you:
template<typename T>
C<T> make_C(T &t) {
return C<T>(t);
}
And then you can write:
auto c = make_C(s);
This is not the answer. But maybe this helps somebody who stumbled upon this question or even help somebody to actually find the answer.
In contrast to the original question I added the static member variable S.i.
template<int I> struct S {
static constexpr int i = I;
int get() { return 42 + I; }
};
template<int I, S<I>& n> struct C
{
int get() {
return n.get();
}
};
S<42> s;
int main()
{
C<s.i, s> c;
return c.get();
}
This is not the answer, because still two template arguments are required in order to instantiate the class template C.
This compiles with C++11.

static function in template struct not using default template argument

I have this code which I wasn't able to compile and I was wondering if there's a way around it. Error is - Argument list for class template "a" is missing.
//not compiling one
template <typename T = int>
struct a {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T a<T>::x;
int main() {
int b = a::function(5);
return 0;
}
.
//compiling one
template <typename T = int>
struct a {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T a<T>::x;
int main() {
int b = a<int>::function(5);
return 0;
}
Why can it not use the template argument we passed on default and how can we fix that without entering the template parameter?
The default template parameter int can be used without specifying it, you just need to specify that a is a template:
int b = a<>::function(5);
// ^^
is there a way to do without a<>, just a?
In case your class template a is only intended to provide utility static functions and not act as an object (with state), you could use delegation via a function template, which returns a (dummy) a object, followed by using the fact that an object of a given type, say A, can invoke non-static as well as static member functions.
namespace detail {
template <typename T = int>
struct AImpl {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T AImpl<T>::x;
} // namespace detail
template<typename T = int>
constexpr detail::AImpl<T> a() { return {}; }
int main() {
const auto b_int = a().function(5);
const auto b_char = a<char>().function('a');
(void)b_int; (void)b_char;
}
If you in fact always wants to use deduction and never actually specify the type of the type template parameter (when other than int), you could exchange class template and its static data member and member function by a single function template that wraps a variable with static storage duration:
#include <type_traits>
template<typename T>
T function(T number) {
static T x;
return x = number;
}
int main() {
const auto b_int = function(5);
const auto b_char = function('a');
static_assert(std::is_same_v<decltype(b_int), const int>, "");
static_assert(std::is_same_v<decltype(b_char), const char>, "");
(void)b_int; (void)b_char;
}
This would be an entirely different (and more implicit) API, however.

Operator overloading on an enum nested in a class

The problem
Given the following piece of code :
template <typename T>
struct dummy {
enum enumenum { a = 1, b = 2, c = 4 };
};
int main() {
// I want this line to expands as :
// dummy<double>::enumenum a = operator~(dummy<double>::a);
auto a = ~dummy<double>::a;
}
How do you overload operators on enumenum ?
I'm using std C++14.
What I tried
A naive implementation:
template <typename T>
typename dummy<T>::enumenum
operator~(typename dummy<T>::enumenum a) {
return static_cast<typename dummy<T>::enumenum>(operator~(a));
}
Unfortunately the line in question expands as:
int a = ~static_cast<int>(dummy<double>::a);
Which means that the operator was not used (this is the default behavior).
Is it because ADL could not find the right operator~() in the struct namespace (is that even a thing ?) ?
Then I tried: (note the friend)
template <typename T>
struct dummy {
enum enumenum { a, b, c };
friend enumenum operator~(enumenum a) {
return static_cast<enumenum>(~a);
}
};
This actually works and expands as:
template <>
struct dummy<double> {
enum enumenum {
a = static_cast<unsigned int>(1),
b = static_cast<unsigned int>(2),
c = static_cast<unsigned int>(4)
};
friend inline dummy<double>::enumenum operator~(dummy<double>::enumenum a) {
return static_cast<dummy<double>::enumenum>(operator~(a));
}
};
int main()
{
dummy<double>::enumenum a = operator~(dummy<double>::a);
}
This is the behavior I want. Except, what if I don't want to define the operator in the class body.
So I tried :
template <typename T>
struct dummy {
enum enumenum { a = 1, b = 2, c = 4 };
// if inline : inline function 'operator~' is not defined [-Wundefined-inline]
// and adding inline to the template below does not help
friend enumenum operator~(enumenum a);
};
template <typename T>
typename dummy<T>::enumenum
operator~(typename dummy<T>::enumenum a) {
return static_cast<typename dummy<T>::enumenum>(~a);
}
int main() {
auto a = ~dummy<double>::a;
}
The code above expands as:
template<>
struct dummy<double>
{
enum enumenum
{
a = static_cast<unsigned int>(1),
b = static_cast<unsigned int>(2),
c = static_cast<unsigned int>(4)
};
friend dummy<double>::enumenum operator~(dummy<double>::enumenum a);
};
int main()
{
dummy<double>::enumenum a = operator~(dummy<double>::a);
}
This compiles, but does not link!
Edit: I believe it does not link because the template is not instantiated thus failing at link time (similarly to the naive implementation above).
Conclusion
Even though I somehow found a way to achieve what I wanted, what if I don't want to define the operator inside the class definition.
Thanks in advance.
This compiles, but does not link!
Compile but doesn't link because you declare a non-template operator (it's inside a template struct but isn't a template function)
friend enumenum operator~(enumenum a);
and you define a template one
template <typename T>
typename dummy<T>::enumenum
operator~(typename dummy<T>::enumenum a) {
return static_cast<typename dummy<T>::enumenum>(~a);
}
and a template definition can't match a non-template declaration.
You could try to declare the function as a template one
template <typename T>
struct dummy
{
enum enumenum { a = 1, b = 2, c = 4 };
template <typename U>
friend typename dummy<U>::enumenum
operator~ (typename dummy<U>::enumenum const & a);
};
template <typename T>
typename dummy<T>::enumenum
operator~ (typename dummy<T>::enumenum const & a)
{ return static_cast<typename dummy<T>::enumenum>(~a); }
int main ()
{
auto a = ~dummy<double>::a;
}
but, unfortunately, this compile but, calling the ~ operator as follows
~dummy<double>::a;
isn't called the template function because the template parameter T can't be deduced because is in not-deduced-context (before the last ::), as pointed by Benny K in a comment.
So, instead the template operator~(), the dummy<double>::a is converted to int and the ~ operator for int is applied (with type result int).
You can verify this point explicitly calling a function operator~()
auto a = operator~(dummy<double>::a);
You should get a "no matching function for call to 'operator~'" error (with note "note: candidate template ignored: couldn't infer template argument 'T'") or something similar.
To make this solution works, you have to explicate the type of the class, to avoid the template deduction
auto a = operator~<double>(dummy<double>::a);
and, now, you can verify that a is a dummy<double>::enumenum
static_assert( std::is_same<decltype(a), dummy<double>::enumenum>::value, "!" );
But, obviously, this isn't a satisfactory solution (and very dangerous, if you forget to avoid the simple use of ~).
Otherwise you can define the operator as non-template
template <typename T>
struct dummy
{
enum enumenum { a = 1, b = 2, c = 4 };
friend enumenum operator~ (enumenum const & a);
};
dummy<double>::enumenum
operator~(dummy<double>::enumenum const & a)
{ return static_cast<dummy<double>::enumenum>(~a); }
int main ()
{
auto a = ~dummy<double>::a;
}
but you have to define a different operator for every dummy<T> type.
IMHO the most simple, safe and elegant solution is your working one: declare/define the operator inside the struct.

Passing a type specifier to a template function

I am trying to replace a Macro in the form of
#define FOO(object, typeSpecifier) object.f<typeSpecifier>()
How can i write an equivalent template function that takes a type specifier to call object.f<typeSpecifier>() with?
I.e. pass a custom type with specifier Mytype and an object object to f like f(object, MyType)
Edit there were some misleading mistakes in simplified code before the answers were made. I.e. the macro had the same name of the function it replaces, this was wrong
Functions cannot take type as parameter, so you cannot do with function:
f(foo, int); // Not possible
You can wrap the type:
template <typename> struct tag{};
f(foo, tag<int>{});
but then you have to change the calling code.
So you have to keep the macro if you don't want to change the calling sites.
If you can change call sites to f<int>(foo);, then you may use:
template <typename T, typename Object>
decltype(auto) f(Object&& object)
{
return std::forward<Object>(object).template <T>()
}
A zero-overhead way would be to use tag dispatch:
#include <iostream>
template<class Type> struct tag {};
template<class Object, class Tag>
decltype(auto) f(Object&& o, tag<Tag>)
{
return o.template f<Tag>();
}
struct X {
template<class T> auto f() {
return T(0);
}
};
template<> auto X::f<int>() {
std::cout << "an int" << std::endl;
return 0;
}
template<> auto X::f<double>() {
std::cout << "a double" << std::endl;
return 0;
}
int main()
{
X x;
f(x, tag<int>());
f(x, tag<double>());
}
You can rely on template argument deduction like:
template<typename T, typename U>
void f(T& object, const U& typeSpecifier) {
object.template f<U>();
}
In this case you can call the f function e.g. as follows:
f(foo, std::string{});
As the comments below point out, my example works only for default constructable types, therefore using a helper type as shown in the accepted answer is a better approach:
template<typename>
struct type {
};
template<typename T, typename U>
void f(T& object, type<U>) {
object.template f<U>();
}
Usage with c++98:
int main() {
MyType foo;
f(foo, type<std::string>());
}

Passing in member variables of a specified class

I want to pass in the name of a member variable. I thought I could do this by
template <typename T::*>
void SetVal(T::* newval)
{
};
This obviously doesn't work, but hopefully gets across what I'm trying to do. I want to be able to set a certain member variable of the templated class.
You can always put compilation-defined constant as template arguments. So here that would be:
template <typename T, typename R, R T::* member>
R& SetVal(T& t, const R& value)
{
t.*member = value;
return t.*member;
}
struct A
{
int a;
};
int main()
{
A a;
SetVal<A,int,&A::a>(a, 10);
return 0;
}