constexpr自动函数中的constexpr-if-else体能够返回不同类型吗?

11

我正在尝试编写一个函数,根据枚举的运行时值将一组类型映射到值。我意识到根据枚举的运行时值返回不同的类型是不可能的,因为编译器将无法知道要分配多少堆栈空间。但是,我正在尝试将其作为constexpr函数编写,使用新的if-constexpr功能来实现此操作。

我从clang中得到了一个错误,指出我正在使用非法指定的模板参数。有人看到如何实现这个问题吗?

编辑:这里有一个更易于理解的版本,更简洁地演示了我的问题:http://coliru.stacked-crooked.com/a/2b9fef340bd167a8

旧代码:

#include <cassert>
#include <tuple>
#include <type_traits>

namespace
{

enum class shape_type : std::size_t
{
  TRIANGLE = 0u,
  RECTANGLE,
  POLYGON,
  CUBE,
  INVALID_SHAPE_TYPE
};

template<std::size_t T>
struct shape {
};

using triangle = shape<static_cast<std::size_t>(shape_type::TRIANGLE)>;
using rectangle = shape<static_cast<std::size_t>(shape_type::RECTANGLE)>;
using polygon = shape<static_cast<std::size_t>(shape_type::POLYGON)>;
using cube = shape<static_cast<std::size_t>(shape_type::CUBE)>;

template<std::size_t A, std::size_t B>
static bool constexpr same() noexcept { return A == B; }

template<std::size_t ST>
static auto constexpr make_impl(draw_mode const dm)
{
  if constexpr (same<ST, shape_type::TRIANGLE>()) {
    return triangle{};
  } else if (same<ST, shape_type::RECTANGLE>()) {
    return rectangle{};
  } else if (same<ST, shape_type::POLYGON>()) {
    return polygon{};
  } else if (same<ST, shape_type::CUBE>()) {
    return cube{};
  } else {
    assert(0 == 5);
  }
}

static auto constexpr make(shape_type const st, draw_mode const dm)
{
  switch (st) {
      case shape_type::TRIANGLE:
        return make_impl<shape_type::TRIANGLE>(dm);
      case shape_type::RECTANGLE:
        return make_impl<shape_type::RECTANGLE>(dm);
      case shape_type::POLYGON:
        return make_impl<shape_type::POLYGON>(dm);
      case shape_type::CUBE:
        return make_impl<shape_type::CUBE>(dm);
      case shape_type::INVALID_SHAPE_TYPE:
        assert(0 == 17);
  }
}

} // ns anon

////////////////////////////////////////////////////////////////////////////////////////////////////
// demo
int main()
{
}

错误:

/home/benjamin/github/BoomHS/main.cxx:42:6: warning: constexpr if is a
C++1z extension [-Wc++1z-extensions]

if constexpr (same<ST, shape_type::TRIANGLE>()) {
        ^ /home/benjamin/github/BoomHS/main.cxx:59:16: error: no matching function for call to 'make_impl'
        return make_impl<shape_type::TRIANGLE>(dm);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/benjamin/github/BoomHS/main.cxx:40:23: note: candidate template
ignored: invalid explicitly-specified argument for template parameter
'ST' static auto constexpr make_impl(draw_mode const dm)
                      ^ /home/benjamin/github/BoomHS/main.cxx:61:16: error: no matching function for call to 'make_impl'
        return make_impl<shape_type::RECTANGLE>(dm);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/benjamin/github/BoomHS/main.cxx:40:23: note: candidate template
ignored: invalid explicitly-specified argument for template parameter
'ST' static auto constexpr make_impl(draw_mode const dm)
                      ^ /home/benjamin/github/BoomHS/main.cxx:63:16: error: no matching function for call to 'make_impl'
        return make_impl<shape_type::POLYGON>(dm);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/benjamin/github/BoomHS/main.cxx:40:23: note: candidate template
ignored: invalid explicitly-specified argument for template parameter
'ST' static auto constexpr make_impl(draw_mode const dm)
                      ^ /home/benjamin/github/BoomHS/main.cxx:65:16: error: no matching function for call to 'make_impl'
        return make_impl<shape_type::CUBE>(dm);
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/benjamin/github/BoomHS/main.cxx:40:23: note: candidate template
ignored: invalid explicitly-specified argument for template parameter
'ST' static auto constexpr make_impl(draw_mode const dm)

3
你的模板参数中为什么都用了 std::size_t - mascoj
这看起来像是一个过度设计的工厂方法。 - Swift - Friday Pie
你的错误与 if constexpr 没有任何关系(并不是你正确使用它)。 - T.C.
抱歉,这是之前我尝试匹配类型时留下的。 - Short
1个回答

15

是的,您尝试的内容是允许的。我在N4606中找到了两个相关的摘录:

6.4.1/2 [stmt.if]

如果 if 语句是 if constexpr 的形式[...] 如果转换后的条件值为 false,则第一个子语句是一个废弃语句,否则第二个子语句(如果存在)是一个废弃语句。

constexpr if 中指示未选取的分支是一个被废弃的语句。此外,auto 函数只考虑非废弃的返回语句来推断返回类型。

7.1.7.4/2 [dcl.spec.auto](强调是我的)

[...] 如果函数的声明返回类型包含占位符类型,则函数的返回类型从函数体中的非废弃返回语句(如果有的话)中推导出来(6.4.1)。

简而言之,以下代码适用于 gccclang head。

namespace {

enum class shape_type { TRIANGLE, RECTANGLE, CIRCLE};

template<shape_type>
struct shape { };

using triangle = shape<shape_type::TRIANGLE>;
using rectangle = shape<shape_type::RECTANGLE>;
using circle = shape<shape_type::CIRCLE>;

template<shape_type ST>
constexpr auto make() {
  if constexpr (ST == shape_type::TRIANGLE) {
    return triangle{};
  } else if constexpr (ST == shape_type::RECTANGLE) {
    return rectangle{};
  } else if constexpr (ST == shape_type::CIRCLE) {
    return circle{};
  } 
}

}

int main() {
  auto t = make<shape_type::TRIANGLE>();
  auto r = make<shape_type::RECTANGLE>();
  auto c = make<shape_type::CIRCLE>();
}

谢谢您的回复!那几乎是一样的,但我想能够将类型存储在变量中。这是稍微修改过的版本,以展示我的意思: http://coliru.stacked-crooked.com/a/2b9fef340bd167a8 - Short
你可以将类型存储在constexpr shape_type中,它可以作为模板参数使用。 - Ryan Haining
我不确定你的意思,我尝试了“auto constexpr t = make(shape_type::TRIANGLE);”,并且得到了同样的错误。你所说的把它存储在constexpr shape_type中是指这个吗? - Short
我认为你想要将make函数的参数存储在一个变量中,而不是枚举字面值。你可以使用上面的代码,并执行constexpr auto shape = shape_type::TRIANGLE; auto t = make<shape>();但是因为constexpr函数也处理非constexpr参数,所以你不能使用你所要求的语法来调用它(除非17年有什么改变我不知道)。 - Ryan Haining
简短回答,我不明白你在例子中所说的“将类型存储在变量中”,你试图存储的“类型”是什么? - Ryan Haining

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接