I am looking for a way to define a variable with a type depending on the type of the member of a type my class is templated on, with the additional caveat that the member variable might not exist. As I only access the variable in constexpr
blocks if the member does exist, I don't care about declaring it or what type it is otherwise. However, I learned that typename conditional<hasMember<T>, decltype(T::member), int>
does not work, as the not-used branch still needs to compile.
Here is an example of what I was hoping to get to work:
#include <iostream>
using namespace std;
struct X{};
struct Y{string member;};
template<class T>
concept hasMember = requires (T t) {t.member;};
template<class T>
struct A{
void hi(T a) {
// stuff
typename conditional<hasMember<T>, decltype(T::member), int /*member is unused if hasMember<T> is false, so I don't care what type it is or if it exists*/>::type member;
if constexpr (hasMember<T>){
member = a.member;
}
// stuff
if constexpr (hasMember<T>){
std::cout << member<< std::endl;
}
};
};
int main() {
X x;
Y y{"hi"};
// Does not compile
// A<X> ax;
// ax.hi(x);
// Compiles and runs fine
A<Y> ay;
ay.hi(y);
return 0;
}
@TedLyngmo's solution is solid, and there are more alternatives. You can simply rely on the fact that the compiler lazily instantiates all templates already, and define a class template:
template <typename T>
struct member_type {
using type = decltype(T::member);
};
As long as you don't access ::type
, no instantiation of member_type
takes place.
This can be exploited in std::conditional_t
:
using M = std::conditional_t<hasMember<T>, member_type<T>, std::type_identity<int>>::type;
M member;
Instead of conditionally selecting decltype(T::member)
and int
, you select a class template for which ::type
contains those types. Then, access ::type
only for the chosen class template, where std::type_identity
is used as a fallback.
You could create an old-school type trait to get the type instead:
template<class T, class O>
struct member_or {
static O test(...);
template<class U = T>
static auto test(int) -> decltype(U::member);
using type = decltype(test(0));
};
template<class T, class O>
using member_or_t = member_or<T, O>::type;
Your A
implementation could then be:
template <class T>
struct A {
void hi(T a) {
// stuff
member_or_t<T, int> member; // <- now no problem
if constexpr (hasMember<T>) {
member = a.member;
}
// stuff
if constexpr (hasMember<T>) {
std::cout << member << std::endl;
}
};
};
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