Reputation: 5462
Maybe I missed something, but I can't find any hints: is there a constexpr ternary operator in C++17 equivalent to constexpr-if?
template<typename Mode>
class BusAddress {
public:
explicit constexpr BusAddress(Address device) :
mAddress(Mode::write ? (device.mDevice << 1) : (device.mDevice << 1) | 0x01) {}
private:
uint8_t mAddress = 0;
};
Upvotes: 82
Views: 23199
Reputation: 1
Just like how std::conditional could be
template <bool B, typename T, typename F>
struct conditional { using type = T; };
template <typename T, typename F>
struct conditional<false, T, F> { using type = F; };
template <bool B, typename T, typename F>
using conditional_t = typename conditional<B, T, F>::type;
you can write
template <bool B, auto T, auto F>
struct conditional_value;
template <bool B, typename val_t, val_t T, val_t F>
struct conditional_value<B, T, F> { static constexpr val_t value = T; };
template <typename val_t, val_t T, val_t F>
struct conditional_value<false, T, F> { static constexpr val_t value = F; };
template <bool B, auto T, auto F>
constexpr auto conditional_v = conditional_value<B, T, F>::value;
and then go on to use it like
template <typename T>
struct my_class
{
static constexpr num_elems = conditional_v<sizeof(T) > 8, 16, 8>;
std::array<int, num_elems> fields;
};
and if you really want to go wild, you can let the types diverge, but you'll probably want a convertible requirement in the struct or else use common_type
template <bool B, auto T, auto F>
struct conditional_value;
template <bool B, typename tval_t, tval_t T, typename fval_t, fval_t F>
struct conditional_value<B, T, F>
{
// static_assert(std::is_convertible_v<tval_t,fval_t> && std::is_convertible_v<fval_t,tval_t>, "need to be able to convert");
// using type = std::common_type_t<tval_t, fval_t>;
// static constexpr type value = T;
static constexpr tval_t value = T;
};
template <typename tval_t, tval_t T, typename fval_t, fval_t F>
struct conditional_value<false, T, F>
{
// static_assert(std::is_convertible_v<tval_t,fval_t> && std::is_convertible_v<fval_t,tval_t>, "need to be able to convert");
// using type = std::common_type_t<tval_t, fval_t>;
// static constexpr type value = T;
static constexpr fval_t value = F;
};
Upvotes: 0
Reputation: 473537
You seem to be acting under the belief that if constexpr
is a performance optimization. It isn't. If you put a constant expression in a ?:
clause, any compiler worth using will figure out what it resolves to and remove the condition. So the code as you have written it will almost certainly compile down to a single option, for a particular Mode
.
The principle purpose of if constexpr
is to eliminate the other branch entirely. If used in a template where the condition is based on a template parameter, the compiler skips most of the checks for validity of the code in that branch. This would be for something where you if constexpr(is_default_constructible_v<T>)
, and if it is true, you do T()
. With a regular if
statement, if T
isn't default constructible, T()
will still have to be valid code even if the surrounding if
clause is a constant expression. if constexpr
removes that requirement; the compiler will discard statements that are not in the other condition.
This becomes even more complicated for ?:
, because the expression's type is based on the types of the two values. As such, both expressions need to be legal expressions, even if one of them is never evaluated. A constexpr
form of ?:
would presumably discard the alternative that is not taken at compile time. And therefore the expression's type should really only be based on one of them.
That a very different kind of thing.
Upvotes: 42
Reputation: 1292
Accepted answer can also be translated into a template function for convenience:
#include <type_traits>
#include <utility>
template <bool cond_v, typename Then, typename OrElse>
decltype(auto) constexpr_if(Then&& then, OrElse&& or_else) {
if constexpr (cond_v) {
return std::forward<Then>(then);
} else {
return std::forward<OrElse>(or_else);
}
}
// examples
struct ModeFalse { static constexpr bool write = false; };
struct ModeTrue { static constexpr bool write = true; };
struct A {};
struct B {};
template <typename Mode>
auto&& test = constexpr_if<Mode::write>(A{}, B{});
static_assert(std::is_same_v<A&&, decltype(test<ModeTrue>)>);
static_assert(std::is_same_v<B&&, decltype(test<ModeFalse>)>);
const A a;
B b;
template <typename Mode>
auto&& test2 = constexpr_if<Mode::write>(a, b);
static_assert(std::is_same_v<const A&, decltype(test2<ModeTrue>)>);
static_assert(std::is_same_v<B&, decltype(test2<ModeFalse>)>);
Upvotes: 0
Reputation: 303147
No, there is no constexepr
conditional operator. But you could wrap the whole thing in a lambda and immediately evaluate it (an IIFE):
template<typename Mode>
class BusAddress {
public:
explicit constexpr BusAddress(Address device)
: mAddress([&]{
if constexpr (Mode::write) {
return device.mDevice << 1;
}
else {
return (device.mDevice << 1) | 0x01;
}
}())
{ }
private:
uint8_t mAddress = 0;
};
It may not be the sexiest code ever, but it gets the job done. Note that lambdas are constexpr
by default where possible as of N4487 and P0170.
Upvotes: 65