0

I tried to write three types of meta-programming template to check a class object is able to convert to an int or not.

#include <iostream>
using namespace std;

template<typename T>
struct meta1
{
    static char(&resolve(int))[2];
    static char resolve(...);
    enum { value = sizeof(resolve(T())) - 1 };
};

template<typename T>
struct meta2
{
    struct result_yes { char _[2]; };
    typedef char result_no;
    static result_yes resolve(int);
    static result_no resolve(...);
    enum { value = sizeof(resolve(T())) - 1 };
};

template<typename T>
struct meta3
{
    static constexpr bool resolve(int) { return true; }
    static constexpr bool resolve(...) { return false; }
    enum { value = resolve(T()) }; // error C2131: expression did not evaluate to a constant
};

#define CHECK(FUNC_NAME) \
cout << "Checking " #FUNC_NAME << endl; \
cout << FUNC_NAME<int>::value << endl; \
cout << FUNC_NAME<string>::value << endl; \

int main()
{
    CHECK(meta1)
    CHECK(meta2)
    CHECK(meta3)
}

and an error occured when using constexpr, error C2131: expression did not evaluate to a constant How can I fix this, and why this happened?

Thank you.

1
  • 1
    "call" inside sizeof is in unevaluated context (that's why you might omit definition). enum { value = resolve(T()) }; is in evaluated context, and string{} is unusable in constant expression until C++20 (and currently not yet supported). Commented Jun 24, 2021 at 14:01

2 Answers 2

2

constexpr marked functions don't actually need to be called at compile time. They can be, but they can also work at runtime, if their arguments are runtime things. In your case, an std::string would not work for result at compile time (since std::string can't be evaluated at compile-time - no constexpr constructor).

int can so for that it works.

the other methods work just fine since they don't use constexpr functions

EDIT: you could, maybe, benefit from std::declval instead of using T() link

EDIT2: would this not be an option or is this for learning purposes?

Sign up to request clarification or add additional context in comments.

4 Comments

Yes, this is for learning purposes. And it is a nice option using std::is_convertible. Thank you for your advise.
@colinzcli maybe take a look at: link
I try to replace T() to declval<T>(), but same error(C2131) occured. This time even only passing int type to check convertable. Did I use it in a wrong way?
@colinzcli sorry, my bad, I think you're always evaluating with result, you can only use std::declval in unevaluated contexts... like sizeof or decltype so it's useless here, at least it seems so
0

Not sure why this isn't working like it does in the other cases but as a work around you can turn meta3 into

template<typename T>
struct meta3
{
    enum { value = std::is_same_v<T, int> };
};

Which will compile using C++17 or higher. You can use std::is_same<T, int>::value if you want to suport C++11 or C++14.

1 Comment

I think it doesn't work because constexpr functions can also be called at runtime with runtime evaluated arguments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.