【问题标题】:Can constexpr-if-else bodies return different types in constexpr auto function?constexpr-if-else 主体能否在 constexpr auto 函数中返回不同类型?
【发布时间】:2016-12-21 22:38:07
【问题描述】:

我正在尝试编写一个函数,该函数根据枚举的运行时值将值枚举映射到一组类型。我意识到您不能根据枚举的运行时值返回不同的类型,因为编译器不知道要分配多少堆栈空间。但是我正在尝试将其编写为 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)

【问题讨论】:

  • 模板参数中的所有std::size_t 是什么意思?
  • 这看起来像是一种过度设计的工厂方法。
  • 您的错误与if constexpr无关(不是您使用正确)。
  • 抱歉,我之前尝试匹配类型时它就在那里。

标签: c++ c++17 multiple-return-values if-constexpr


【解决方案1】:

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

6.4.1/2 [stmt.if]

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

constexpr if 中指示未采用的分支是一个废弃的语句。此外,自动函数只考虑非丢弃的返回语句来推导返回类型

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

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

简化后,以下代码适用于 gccclang 头。

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>();
}

【讨论】:

  • 感谢您的回复!这几乎是一回事,但我希望能够将类型存储在变量中。这是您的稍作修改的版本,以说明我的意思:coliru.stacked-crooked.com/a/2b9fef340bd167a8
  • @Short,您可以将类型存储在 constexpr shape_type 中,用作模板参数
  • 我不确定你的意思,我试过 "auto constexpr t = make(shape_type::TRIANGLE); 并得到同样的错误。这就是你将它存储在 constexpr shape_type 中的意思吗? ?
  • 我认为您要求将 make 的参数存储在变量而不是枚举文字中。您可以使用上面的代码,然后执行 constexpr auto shape = shape_type::TRIANGLE; auto t = make&lt;shape&gt;(); 但因为 constexpr 函数也处理非 constexpr 参数,所以您不能使用您要求的语法调用它(除非我不知道 17 的某些内容发生了变化) .
  • @Short 我不知道您的示例中的“将类型存储在变量中”是什么意思,“类型”是什么意思"你要存储吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-24
  • 1970-01-01
  • 1970-01-01
  • 2018-01-23
  • 1970-01-01
相关资源
最近更新 更多