Я пытаюсь написать функцию, которая отображает перечисление значений в набор типов на основе значения времени выполнения перечисления. Я понимаю, что вы не можете возвращать различные типы на основе значения времени выполнения перечисления, потому что компилятор не будет знать, сколько стекового пространства выделить. Однако я пытаюсь написать это как функцию constexpr, используя для этого новую функциональность if-constexpr.
Я получаю сообщение об ошибке от clang с жалобой на использование неверно указанного параметра шаблона. Кто-нибудь видит, как это реализовать?
edit: вот более простая версия, демонстрирующая мою проблему более кратко:
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)
Да, то, что вы пытаетесь, разрешено. Два соответствующих выдержки, которые я нашел в N4606 являются:
6.4.1 / 2 [stmt.if]
Если оператор if имеет форму if constexpr […] Если значение
преобразованное условие ложно, первое подзаголовок является оператором, от которого отказываются, в противном случае второе подзаголовок, если оно присутствует, является оператором, от которого отказываются
Указание на неиспользованную ветку в constexpr если является отброшенным статусом. Кроме того, автоматические функции учитывают только не возвращенные операторы возврата для определения типа возвращаемого значения.
7.1.7.4/2 [dcl.spec.auto] (выделено мной)
[…] Если объявленный тип возврата функции содержит тип заполнителя, тип возврата функции выводится из неотбрасываемых деклараций возврата, если есть, в теле функции (6.4.1).
Упрощенно, следующий код работает на обоих НКУ а также лязг голова.
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>();
}
Других решений пока нет …