The following code works as expected:
template <int N> struct Int {
constexpr auto dec() const -> Int<N - 1> {
return {};
}
constexpr operator int() const {
return N;
}
};
constexpr auto fib(Int<0>) -> Int<0> {
return {};
}
template <int N> constexpr auto fib(Int<N> n) -> decltype(fib(n.dec())) {
return {};
}
static_assert(fib(Int<2>()) == 0);
However, if I introduce a namespace for Int
, it suddenly fails on both GCC and Clang:
namespace foo {
template <int N> struct Int {
constexpr auto dec() const -> Int<N - 1> {
return {};
}
constexpr operator int() const {
return N;
}
};
}
template <int N> using Int = foo::Int<N>;
constexpr auto fib(Int<0>) -> Int<0> {
return {};
}
template <int N> constexpr auto fib(Int<N> n) -> decltype(fib(n.dec())) {
return {};
}
static_assert(fib(Int<2>()) == 0);
With the following error message:
<source>:21:15: error: no matching function for call to 'fib'
21 | static_assert(fib(Int<2>()) == 0);
| ^~~
<source>:14:16: note: candidate function not viable: no known conversion from 'Int<2>' to 'Int<0>' for 1st argument
14 | constexpr auto fib(Int<0>) -> Int<0> {
| ^ ~~~~~~
<source>:17:33: note: candidate template ignored: substitution failure [with N = 2]: call to function 'fib' that is neither visible in the template definition nor found by argument-dependent lookup
17 | template <int N> constexpr auto fib(Int<N> n) -> decltype(fib(n.dec())) {
| ^ ~~~
Can someone explain what is going on here? Why does introducing a namespace create a compiler error?
The following code works as expected:
template <int N> struct Int {
constexpr auto dec() const -> Int<N - 1> {
return {};
}
constexpr operator int() const {
return N;
}
};
constexpr auto fib(Int<0>) -> Int<0> {
return {};
}
template <int N> constexpr auto fib(Int<N> n) -> decltype(fib(n.dec())) {
return {};
}
static_assert(fib(Int<2>()) == 0);
However, if I introduce a namespace for Int
, it suddenly fails on both GCC and Clang:
namespace foo {
template <int N> struct Int {
constexpr auto dec() const -> Int<N - 1> {
return {};
}
constexpr operator int() const {
return N;
}
};
}
template <int N> using Int = foo::Int<N>;
constexpr auto fib(Int<0>) -> Int<0> {
return {};
}
template <int N> constexpr auto fib(Int<N> n) -> decltype(fib(n.dec())) {
return {};
}
static_assert(fib(Int<2>()) == 0);
With the following error message:
<source>:21:15: error: no matching function for call to 'fib'
21 | static_assert(fib(Int<2>()) == 0);
| ^~~
<source>:14:16: note: candidate function not viable: no known conversion from 'Int<2>' to 'Int<0>' for 1st argument
14 | constexpr auto fib(Int<0>) -> Int<0> {
| ^ ~~~~~~
<source>:17:33: note: candidate template ignored: substitution failure [with N = 2]: call to function 'fib' that is neither visible in the template definition nor found by argument-dependent lookup
17 | template <int N> constexpr auto fib(Int<N> n) -> decltype(fib(n.dec())) {
| ^ ~~~
Can someone explain what is going on here? Why does introducing a namespace create a compiler error?
Share Improve this question asked Mar 15 at 10:52 eyelasheyelash 3,78827 silver badges34 bronze badges 3 |1 Answer
Reset to default 4Functions can be recursive, of course, but they can’t be recursive in their own return type (even in the rare instance that it would be meaningful) because name lookup doesn’t consider them:
auto f() -> std::conditional_t<true,void,decltype(f())*>; // f undeclared
With function templates, one can construct reasonable-looking recursions like yours, but name lookup still can’t find the same template—except via ADL, of course, which is the usual culprit in weird behavior like this.
foo::Int
parameter? Will it find any there? See Argument Dependent Lookup – BoP Commented Mar 15 at 13:45fib<0>
if overload doesn't select it; but instantiate it lead to (negative) overflow. – Jarod42 Commented Mar 15 at 14:16