1

I want my class use another implementation for types don't have constexpr constructor.

like this:

template <typename A>
class foo
{
public:

    // if A has constexpr constructor
    constexpr foo() :_flag(true) { _data._a = A(); }
    // else
    constexpr foo() : _flag(false) { _data.x = 0; }

    ~foo(){}

    bool _flag;
    union _data_t
    {
        _data_t() {} // nothing, because it's just an example
        ~_data_t() {}
        A _a;
        int x;
    }_data;
};

To achieve what the title says, I try this:

template<typename _t, _t = _t()>
constexpr bool f()
{
    return true;
}
template<typename _t>
constexpr bool f()
{
    return false;
}

It works well for types haven't constexpr constructor. But for other types it causes a compile error with ambiguous overloads.

so how can I check?

ipnah
  • 125
  • 7
  • 4
    why do you need this? I don't see a valid use case for this. Please explain what it is you are trying to accomplish because this looks like a XY problem. – bolov Apr 21 '22 at 12:53
  • @bolov I'm writing a class that uses templates, it stores a value of one of these types, and I want my class to have constexpr constructor if that type has constexpr constructor . – ipnah Apr 21 '22 at 13:01
  • Duplicate of [C++ check if statement can be evaluated constexpr](https://stackoverflow.com/q/55288555/364696)? I'm not voting as such because I don't want to use my dupehammer powers (not sufficiently confident it applies completely), but it looks like it would solve the problem. – ShadowRanger Apr 21 '22 at 13:03
  • 3
    @ipnah: "*I want my class to have constexpr constructor if that type has constexpr constructor .*" You can do that by declaring the constructor `constexpr`. If the member's constructor used by your constructor isn't `constexpr`, then neither will your constructor be. That is, `constexpr` is conditional when applied to a function template, if the thing that would cause the function to not be `constexpr` is based on a template parameter. – Nicol Bolas Apr 21 '22 at 13:11
  • @ShadowRanger I tried changing its code to suit my needs but it always seems to give `false` This is the code: – ipnah Apr 21 '22 at 13:21
  • @NicolBolas oh it did. I suddenly have a new question, if I require my class to use another implementation for this type, what should I do? Seems to be the title. – ipnah Apr 21 '22 at 13:28
  • @ipnah: "*if I require my class to use another implementation for this type, what should I do?*" I don't know what you mean. Can you put a code example in your question? You didn't really catch the point I was making: if your make your class template's constructor `constexpr`, it will be `constexpr` if its members are appropriate for that. – Nicol Bolas Apr 21 '22 at 13:29
  • 1
    @ipnah: The question is *why* you need your type to have radically different behavior based on a `constexpr` constructor for a type? What are you trying to achieve with this "another implementation"? Because you generally don't need that. – Nicol Bolas Apr 21 '22 at 13:31
  • Well, I haven't figured out what this could be used for yet. And I thought about it, it doesn't seem like I can make this class have a constexpr constructor even if I can check. – ipnah Apr 21 '22 at 13:44
  • @ShadowRanger oh I know, I didn't specify the newer c++ version. – ipnah Apr 21 '22 at 13:51

1 Answers1

0

I suppose you can use SFINAE together with the power of the comma operator

Following your idea, you can rewrite your f() functions as follows

template <typename T, int = (T{}, 0)>
constexpr bool f (int)
{ return true; }

template <typename>
constexpr bool f (long)
{ return false; }

Observe the trick: int = (T{}, 0) for the second template argument

This way f() is enabled (power of the comma operator) only if T{} can be constexpr constructed (because (T{}, 0) is the argument for a template parameter), otherwise SFINAE wipe away the first version of f().

And observe that the fist version of f() receive an unused int where the second one receive a long. This way the first version is preferred, when available, calling f() with an int; the second one is selected, as better than nothing solution, when the first one is unavailable (when the first template argument isn't constexpr default constructible).

Now you can construct two template constructors for foo that you can alternatively enable/disable according the fact the template parameter T (defaulted to A) is or isn't constexpr constructible

template <typename T = A,
          std::enable_if_t<f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "constexpr" << std::endl; }

template <typename T = A,
          std::enable_if_t<not f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "not constexpr" << std::endl; }

The following is a full compiling example (C++14 or newer, but you can modify it for C++11):

#include <iostream>
#include <type_traits>

template <typename T, int = (T{}, 0)>
constexpr bool f (int)
{ return true; }

template <typename>
constexpr bool f (long)
{ return false; }

template <typename A>
struct foo
{
  template <typename T = A,
            std::enable_if_t<f<T>(0), std::nullptr_t> = nullptr>
  constexpr foo() { std::cout << "constexpr" << std::endl; }

  template <typename T = A,
            std::enable_if_t<not f<T>(0), std::nullptr_t> = nullptr>
  constexpr foo() { std::cout << "not constexpr" << std::endl; }
};


struct X1 { constexpr X1 () {} };
struct X2 {           X2 () {} };


int main()
{
  foo<X1> f1;  // print "constexpr"
  foo<X2> f2;  // print "not constexpr"
}
max66
  • 65,235
  • 10
  • 71
  • 111
  • A little question: At `template `, My compiler (MSVC) can't make it a constant unless I change `T{}` to `T()` – ipnah May 06 '22 at 10:43