Reputation: 1534
When I try and get the index of a type within a list of types using below, the code compiles and returns the correct value when the else clause is used. However when I skip the else clause and place the return getIndex<T, Ts...>(x + 1);
just after the end of the if clause the code fails to compile as it continues to unwind getIndex<T, Ts...>
recursively resulting in the error shown below. This is the case with gcc
and clang
. Is this expected?
#include <type_traits>
template <typename T, typename U, typename ...Ts>
constexpr int getIndex(int x = 0)
{
if constexpr(std::is_same_v<T,U>)
{
return x;
}
else
{
return getIndex<T, Ts...>(x + 1);
}
}
int main()
{
return getIndex<int, float, double, int, char>();
}
Error when moving return outside of else
getI.cc: In instantiation of ‘int getIndex(int) [with T = int; U = char; Ts = {}]’:
getI.cc:9:32: recursively required from ‘int getIndex(int) [with T = int; U = double; Ts = {int, char}]’
getI.cc:9:32: required from ‘int getIndex(int) [with T = int; U = float; Ts = {double, int, char}]’
getI.cc:14:51: required from here
getI.cc:9:32: error: no matching function for call to ‘getIndex<int>(int)’
9 | return getIndex<T, Ts...>(x + 1);
| ~~~~~~~~~~~~~~~~~~^~~~~~~
getI.cc:3:5: note: candidate: ‘template<class T, class U, class ... Ts> int getIndex(int)’
3 | int getIndex(int x = 0)
| ^~~~~~~~
getI.cc:3:5: note: template argument deduction/substitution failed:
getI.cc:9:32: note: couldn’t deduce template parameter ‘U’
9 | return getIndex<T, Ts...>(x + 1);
| ~~~~~~~~~~~~~~~~~~^~~~~~~
Upvotes: 3
Views: 1010
Reputation: 3651
Peter Taran's answer has already touched on why the code compiles with the else
clause, but he didn't really touch on why the compilation error occurs or how to fix it. Here is a working version of your code:
#include <type_traits>
template <typename T>
constexpr int getIndex(int x = 0)
{
return -1;
}
template <typename T, typename U, typename ...Ts>
constexpr int getIndex(int x = 0)
{
if constexpr (std::is_same_v<T,U>)
{
return x + 1;// NOTE: you have a bug on this line
}
else
{
return getIndex<T, Ts...>(x + 1);
}
}
int main()
{
constexpr int index = getIndex<int, float, double, int, char>();
return 0;
}
Admittedly, I haven't used variadic templates much, but from what I am understanding here, the main problem is in this snippet of your compilation errors:
getI.cc:9:32: error: no matching function for call to ‘getIndex<int>(int)’
Despite having a base case defined in your recursive template, it seems like variadic templates are required to fully unpack. Because of that, you need a getIndex<T>
defined, even if the code is not reached in your example. Adding the single argument getIndex<T>
allowed the code to compile and run.
EDIT: This post is also related, a good read, and is also another possible solution to your issue.
Upvotes: 3
Reputation: 150
As I realized, broken code is:
template <typename T, typename U, typename ...Ts>
constexpr int getIndex(int x = 0)
{
if constexpr(std::is_same_v<T,U>) {
return x;
}
return getIndex<T, Ts...>(x + 1);
}
Accoring to this article:
In a constexpr if statement, the value of condition must be an expression contextually converted to bool, where the conversion is a constant expression. If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.
Note that nothing is said about discarding statements outside if-else clause, so you should expect that recusive getIndex
's will be instantiated.
Upvotes: 2