Black Moses Black Moses - 1 month ago 6
C++ Question

Can static/dynamic/const/reinterpret_cast be used in unevaluated context?

I tried to provide structures for checking is

is (choose cast)-castable to
All four casts would have exact same implementation, expect their names (local-macro-able definitions would be possible, but not necessary). I wrote many check-for operators structures, for example:

#include <iostream>
#include <type_traits>
#include <string>

template<class, class, class, class = void>
struct is_valid_ternary_operator : std::false_type
{ };

template<class T, class S, class R>
struct is_valid_ternary_operator <T, S, R,
std::void_t<decltype(std::declval<T>() ?
std::declval<S>() :
std::declval<R>())>> : std::true_type
{ };

int main()
//true? 1 : 0 //is ok
std::cout << is_valid_ternary_operator<bool, int, int>::value << std::endl;
//true? 1 : std::string("0") //would be error
std::cout << is_valid_ternary_operator<bool, int, std::string>::value << std::endl;
//true? std::string("1") : std::string("0") //ok
std::cout << is_valid_ternary_operator<bool, std::string, std::string>::value << std::endl;
//std::string("1")? 1 : 0 //error
std::cout << is_valid_ternary_operator<std::string, int, int>::value << std::endl;

Live example

Expected output is displayed. But now consider doing the same with casts:

template<class T, class S, class = void>
struct is_static_cast_able : std::false_type
{ };

template<class T, class S>
struct is_static_cast_able<T, S,
(std::declval<T>()))>> : std::true_type
{ };

But it generates errors:

main.cpp:12:84: error: template argument 1 is invalid
(std::declval<T>()))>> : std::true_type
main.cpp:12:94: error: template argument 3 is invalid
(std::declval<T>()))>> : std::true_type


Is using casts in unevaluated context not allowed?


Is using casts in unevaluated context not allowed?

It is allowed, but I would try to reduce the example to a minimal, working one.
Note that casts are expressions, so they are expected to work with operators the operands of which are unevaluated (sizeof, noexcept, decltype, typeid), if not explicitly stated the opposite.

As an example, sizeof is an unevaluated context:

int main() {
    unsigned int i;

Far easier an example, and it works.
The same can be shown using decltype, the operands of which are unevaluated as well:

int main() {
    unsigned int i;
    decltype(static_cast<int>(i)) j = i;

And so on, we can do something similar both with noexcept:

int main() {
    unsigned int i;
    bool b = noexcept(static_cast<int>(i));

And with typeid:

#include <typeinfo>

int main() {
    unsigned int i;
    auto b = typeid(static_cast<int>(i)).name();