I have a class that can inherit from an empty struct or from a struct with some members, depending on a bool. Using that same bool, I am adding an if constexpr block to access the members of the base class, but I am getting a compiler error
struct A{};
struct B{};
struct C{};
template <typename J0, typename J1>
struct HasFriction {
constexpr static bool value = false;
};
template <> struct HasFriction<A,B> {
constexpr static bool value = true;
};
template <bool> struct Friction { };
template <> struct Friction<true> { int value = 4; };
template <typename J0, typename J1>
struct MyStruct : public Friction<HasFriction<J0, J1>::value> {};
int main()
{
if constexpr (HasFriction<A, C>::value) {
MyStruct<A,C> f;
return f.value;
} else {
MyStruct<A,C> no_f;
return 0;
}
}
Why is this not working, and how should I fix it?
Why is this not working and how should I fix it?
In order to work if constexpr (i.e. discard the false statement at compile time), you need to make it template depended.
From cppreference.com, under the if constexpr section:
Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive:
void f() {
if constexpr(false) {
int i = 0;
int *p = i; // Error even though in discarded statement
}
}
Therefore, the fix is to wrap the statement into a template function, as follows:
template<typename T1, typename T2> auto func()
{
if constexpr (HasFriction<T1, T2>::value) {
MyStruct<A, C> f;
return f.value;
}
else {
MyStruct<A, C> no_f;
return 0;
}
}
Now in main()
func<A, B>();
See a demo
Related
I'm trying to understand the reason for the compilation error with my CRTP implementation in this example:
template<class T>
struct Interface
{
static constexpr int func();
static constexpr int func2();
};
template<class T>
struct Default : public Interface<T>
{
constexpr static int impl_func()
{
return 0;
}
constexpr static int impl_func2()
{
return Default<T>::func() + 2; <----------- (A)
}
};
struct Derived : public Default<Derived>
{
static constexpr int impl_func2()
{
// NOTE: Using impl_func instead of func here compiles
return func() + 4; <-------------- (B)
}
};
struct Derived2 : public Default<Derived2>
{
};
template <typename T> constexpr int Interface<T>::func()
{
return T::impl_func();
}
template <typename T> constexpr int Interface<T>::func2()
{
return T::impl_func2();
}
int main()
{
constexpr int ret = Derived::func2();
static_assert(ret == 4);
constexpr int ret2 = Derived2::func2();
static_assert(ret2 == 2);
}
Specifically why is (A) ok while (B) gives me this error:
test3.cpp:45:19: error: constexpr variable 'ret' must be initialized by a constant expression
constexpr int ret = Derived::func2();
^ ~~~~~~~~~~~~~~~~
test3.cpp:29:16: note: undefined function 'func' cannot be used in a constant expression
return func() + 4;
Replacing (B) with return impl_func() + 4 compiles successfully. But neither Derived::func() nor Default<Derived>::func() work. If (B) needs to be impl_func() then is there something different about the fact that (A) is in a templated class that allows it to compile successfully?
With constexpr function, definition should be available when used...
reordering function definition solve the issue:
template<class T>
struct Interface
{
static constexpr int func();
static constexpr int func2();
};
template <typename T> constexpr int Interface<T>::func()
{
return T::impl_func();
}
template <typename T> constexpr int Interface<T>::func2()
{
return T::impl_func2();
}
// Remaining code.
Demo
Let's assume an input template parameter T may or may not have internal variable bar. I am trying to write a struct that returns the value of bar when we have it, and returns some constant when we don't. Here is my attempt:
struct A {
static constexpr unsgined int bar = 20;
hasBar = true;
};
struct B {
hasBar = false;
};
template <typename T, typename std::enable_if<T::hasBar, int>::type>
struct getBar {
static constexpr unsigned int bar = T::bar;
};
template <typename T, typename std::enable_if<!T::hasBar, int>::type>
struct getBar {
static constexpr unsigned int bar = 0;
};
int main() {
getBar<A>::bar; // Expect 20
getBar<B>::bar; //Expect 0
}
I cannot compile this code with C++14. The compiler complains that: "template non-type parameter has a different type".
Why we have such an error and how can I address it?
Class templates can't be overloaded (like function templates); You can use specialization instead. e.g.
template <typename T, typename = void>
struct getBar {
static constexpr unsigned int bar = 0;
};
template <typename T>
struct getBar<T, std::enable_if_t<T::hasBar>> {
static constexpr unsigned int bar = T::bar;
};
LIVE
You can detect whether ::bar exists directly without the need for hasbar
something like ...
#include <type_traits>
#include <iostream>
struct A {
static constexpr unsigned int bar = 20;
};
struct B {
};
template <typename T,typename=void>
struct getBar {
static constexpr unsigned int bar = 0;
};
template <typename T>
struct getBar<T,std::void_t<decltype(T::bar)>> {
static constexpr unsigned int bar = T::bar;
};
int main() {
std::cout << getBar<A>::bar << std::endl; // Expect 20
std::cout << getBar<B>::bar << std::endl; //Expect 0
}
Demo
Another solution that doesn't needs hasBar but simply detect the presence of bar (and also maintain the original type of bar, if different from int)
struct A
{ static constexpr unsigned int bar = 20; };
struct B
{ };
template <typename T>
constexpr auto getBarHelper (int) -> decltype( T::bar )
{ return T::bar; }
template <typename T>
constexpr int getBarHelper (long)
{ return 0; }
template <typename T>
struct getBar
{ static constexpr auto bar { getBarHelper<T>(0) }; };
int main()
{
static_assert( 20u == getBar<A>::bar, "!" );
static_assert( 0 == getBar<B>::bar, "!" );
}
The following code does not compile. Is it even possible to pass an instance of Configuration<t, u> as a template parameter? (to profit from optimization of constant expressions)
API:
template <int t, bool u>
struct Configuration {
static constexpr int commonValue = t;
static constexpr bool debug = u;
};
template <
template<int t, bool u> Configuration<t, u> &conf,
int x
>
class A {
public:
int doSomething() {
if (conf.debug) { // Optimize away if false
// ...
}
return x * conf.commonValue; // Optimize to e.g. 6 with `conf.commonValue` =
// 3, `x` = 2
}
};
The user of the API should be able to do:
int main() {
static constexpr Configuration<3, false> conf;
A<conf, 2> a;
A<conf, 5> b;
A<conf, 8> c;
std::cout << a.doSomething() << std::endl; // 6 (evaluated at compile time!)
std::cout << b.doSomething() << std::endl; // 15
std::cout << c.doSomething() << std::endl; // 24
}
Since the attributes of Configuration are static, you should use a template type parameter1:
template <class ConfT, int x>
class A {
public:
int doSomething() {
if (ConfT::debug) { // Optimize away if false
}
return x * ConfT::commonValue;
}
};
And then:
// Alias (not required):
using conf = Configuration<3, false>;
A<conf, 2> a;
A<conf, 3> b;
If you want non-static members, I don't think this is doable pre-C++17 (without passing template parameters of Configuration to A), but in C++17 you could do1:
template <auto const& conf, int x>
class A {
public:
int doSomething() {
if (conf.debug) { // Optimize away if false
}
return x * conf.commonValue;
}
};
But note that you can only pass references to variables with static storage duration.
1 In the first case, you can restrict the type to Configuration<...> by specializing A, in the second case, you can restrict the type of conf using an extra template parameter with std::enable_if.
You should also make the attributes constexpr in Configuration, not simply const:
template <int t, bool u>
struct Configuration {
static constexpr int commonValue = t;
static constexpr bool debug = u;
};
I need to define a class, foo, with a static member variable template, foo::static_variable_template<T>. This member should only exist when T fulfills certain requirements. For example, when the constexpr static function T::constexpr_static_function() exists. Otherwise, foo::static_variable_template<T> should not exist. Moreover, I would like to be able to test for the existence of foo::static_variable_template<T> at compile-time via SFINAE.
Here is an approximation of what I would like to do:
#include <iostream>
struct foo
{
template<class T>
static constexpr int static_variable_template =
T::constexpr_static_function();
// XXX this works but requires a second defaulted template parameter
// template<class T, int = T::constexpr_static_function()>
// static constexpr int static_variable_template =
// T::constexpr_static_function();
};
struct has_constexpr_static_function
{
static constexpr int constexpr_static_function() { return 42; }
};
struct hasnt_constexpr_static_function
{
};
template<class T, class U,
int = T::template static_variable_template<U>>
void test_for_static_variable_template(int)
{
std::cout << "yes it has\n";
}
template<class T, class U>
void test_for_static_variable_template(...)
{
std::cout << "no it hasn't\n";
}
int main()
{
test_for_static_variable_template<foo, has_constexpr_static_function>(0);
test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}
This approximation nearly works, but only if foo::static_variable_template has a second, defaulted template parameter. Because this second parameter is an implementation detail, I'd like to hide it from the public interface of foo::static_variable_template.
Is this possible in C++17?
I am not sure if your intent is to initialise foo::static_variable_template with 0 if T::constexpr_static_function() is missing or you want to disable it entirely. In case of the former, this might be useful. For example, this (clunky) solution works (requires C++17 for if constexpr; note that your variable is now a function):
#include <iostream>
template <typename T>
class has_func
{
typedef char does;
typedef long doesnt;
template <typename C> static does test( decltype(&C::constexpr_static_function) );
template <typename C> static doesnt test(...);
public:
static constexpr bool value()
{
return sizeof(test<T>(0)) == sizeof(char);
}
};
struct foo
{
template<class T>
static constexpr int static_variable_template()
{
if constexpr (has_func<T>::value())
{
return T::constexpr_static_function();
}
return 0;
}
// XXX this works but requires a second defaulted template parameter
// template<class T, int = T::constexpr_static_function()>
// static constexpr int static_variable_template =
// T::constexpr_static_function();
};
struct has_constexpr_static_function
{
static constexpr int constexpr_static_function() { return 42; }
};
struct hasnt_constexpr_static_function
{
};
template<class T, class U>
void test_for_static_variable_template(...)
{
if constexpr (has_func<U>::value())
{
std::cout << "yes it has\n";
}
else
{
std::cout << "no it hasn't\n";
}
}
int main()
{
std::cout << foo::static_variable_template<has_constexpr_static_function>() << "\n";
std::cout << foo::static_variable_template<hasnt_constexpr_static_function>() << "\n";
/// Original test
test_for_static_variable_template<foo, has_constexpr_static_function>(0);
test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}
Prints
42
0
yes it has
no it hasn't
Tested with clang 5.0.1.
In case you want to disable foo::static_variable_template entirely, you might need to use std::enable_if:
#include <iostream>
template <typename T>
class has_func
{
typedef char does;
typedef long doesnt;
template <typename C> static does test( decltype(&C::constexpr_static_function) );
template <typename C> static doesnt test(...);
public:
static constexpr bool value()
{
return sizeof(test<T>(0)) == sizeof(char);
}
};
struct foo
{
template<class T, typename std::enable_if<has_func<T>::value()>::type ...>
static constexpr int static_variable_template()
{
if constexpr (has_func<T>::value())
{
return T::constexpr_static_function();
}
return 0;
}
// XXX this works but requires a second defaulted template parameter
// template<class T, int = T::constexpr_static_function()>
// static constexpr int static_variable_template =
// T::constexpr_static_function();
};
struct has_constexpr_static_function
{
static constexpr int constexpr_static_function() { return 42; }
};
struct hasnt_constexpr_static_function
{
};
template<class T, class U>
void test_for_static_variable_template(...)
{
if constexpr (has_func<U>::value())
{
std::cout << "yes it has\n";
}
else
{
std::cout << "no it hasn't\n";
}
}
int main()
{
std::cout << foo::static_variable_template<has_constexpr_static_function>() << "\n";
// We can't print this because it doesn't exist.
// std::cout << foo::static_variable_template<hasnt_constexpr_static_function>() << "\n";
/// Original test
test_for_static_variable_template<foo, has_constexpr_static_function>(0);
test_for_static_variable_template<foo, hasnt_constexpr_static_function>(0);
}
In this line of thought, I am not sure if you can disable a static template variable with std::enable_if. To quote the great Riemann, "I have for the time being, after some fleeting vain attempts, provisionally put aside the search for this..."
I am purposely using the very same title as this question because I feel that the answer that was accepted does not account for a problem that I am stuck into.
I am looking for a way to detect if some class has some member variable. It is fundamental to note that I am looking for a variable, not a member function or anything else.
Here is the example provided in the question I linked:
template<typename T> struct HasX {
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
struct A { int x; };
struct B { int X; };
int main() {
std::cout << HasX<A>::value << std::endl; // 1
std::cout << HasX<B>::value << std::endl; // 0
}
But we will get the very same output if we do something like
template<typename T> struct HasX {
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
struct A {
void x()
{
}
};
struct B { int X; };
int main() {
std::cout << HasX<A>::value << std::endl; // 1
std::cout << HasX<B>::value << std::endl; // 0
}
(Please note that in the second example the int x in A was substituted with a member function void x()).
I have no real idea on how to work around this problem. I partially fixed this by doing something like
template <bool, typename> class my_helper_class;
template <typename ctype> class my_helper_class <true, ctype>
{
static bool const value = std :: is_member_object_pointer <decltype(&ctype :: x)> :: value;
};
template <typename ctype> class my_helper_class <false, ctype>
{
static bool const value = false;
};
template <typename T> struct HasX
{
// ...
static bool const value = my_helper_class <sizeof(f <Derived>(0)) == 2, T> :: value;
};
Which actually selects if I am using an object. However, the above doesn't work if there are more overloaded functions with the same name x in my class.
For example if I do
struct A
{
void x()
{
}
void x(int)
{
}
};
Then the pointer is not resolved successfully and the a call to HasX <A> doesn't compile.
What am I supposed to do? Is there any workaround or simpler way to get this done?
The problem is that HasX only checks if the name x exists. The ... gets selected if &C::x is ambiguous (which happens if it matches both in Fallback and T). The ChT<> overload gets selected only if &C::x is exactly Fallback::x. At no point are we actually checking the type of T::x - so we never actually check if x is a variable or function or whatever.
The solution is: use C++11 and just check that &T::x is a member object pointer:
template <class T, class = void>
struct HasX
: std::false_type
{ };
template <class T>
struct HasX<T,
std::enable_if_t<
std::is_member_object_pointer<decltype(&T::x)>::value>
>
: std::true_type { };
If &T::x doesn't exist, substitution failure and we fallback to the primary template and get false_type. If &T::x exists but is an overloaded name, substitution failure. If &T::x exists but is a non-overloaded function, substitution failure on enable_if_t<false>. SFINAE for the win.
That works for all of these types:
struct A {
void x()
{
}
void x(int)
{
}
};
struct B { int X; };
struct C { int x; };
struct D { char x; };
int main() {
static_assert(!HasX<A>::value, "!");
static_assert(!HasX<B>::value, "!");
static_assert(HasX<C>::value, "!");
static_assert(HasX<D>::value, "!");
}