Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does decltype return type fail for recursive template, while return type deduction works just fine?

While working on a C++11 type-set, I tried to implement this function (stripped down to the minimum):

constexpr auto test() -> bool;

template <typename T, typename... Rest>
constexpr auto test() -> decltype(test<Rest...>())
{
  return {};
}

Both gcc and clang choke on this. Clang says:

test.cpp:54:40: error: 'Rest' does not refer to a value
constexpr auto test() -> decltype(test<Rest...>())
                                  ^

gcc complains:

test.cpp:54:44: error: expected primary-expression before ‘...’ token
 constexpr auto test() -> decltype(test<Rest...>())

I guess this is because the variadic version of test is not even fully declared when the decltype looks at it.

However, when I use return type deduction in C++14, this compiles just fine:

constexpr auto test() -> bool;

template <typename T, typename... Rest>
constexpr auto test()
{
  return test<Rest...>();
}

Seems like test is considered sufficiently declared here.

I wonder why this doesn't work for the decltype variant? Even if I turn on C++14 support?

PS: It turns out, that I cannot really call even the C++14 function, so maybe the whole thing is botched...

like image 723
Rumburak Avatar asked Jan 06 '23 05:01

Rumburak


1 Answers

One problem is that your first test overload is not a function template, so can't be called with test<>() syntax.

However, decltype doesn't really work with recursive variadic functions like that, since the return type is part of the declaration of the function, so that overload is not declared when the name is looked up.

You can get around this in C++11 by using template classes instead:

template <typename... Ts>
struct test;

template <typename T, typename... Ts>
struct test<T,Ts...> {
    static decltype(test<Ts...>::value()) value() { return test<Ts...>::value(); }   
};

template <>
struct test<> {
    static bool value() { return true; }
};

Live demo


In C++14 you can make the first overload take a single template parameter and the second take two and a parameter pack:

template <typename T>
constexpr auto test() -> bool { return true; }

template <typename T, typename U, typename... Rest>
constexpr auto test()
{
  return test<U,Rest...>();
}

Live demo

like image 130
TartanLlama Avatar answered Jan 14 '23 15:01

TartanLlama