Issue |
128364
|
Summary |
[Clang++] What type alias should be selected when used together with "deducing this"?
|
Labels |
clang
|
Assignees |
|
Reporter |
BlankSpruce
|
I have an inheritance chain where derived class does something extra before delegating to base class implementation.
<details>
<summary>Here's an example implementing this kind of behaviour with virtual functions</summary>
```c++
#include <print>
struct Solver
{
int state = 0;
int step()
{
if (state++ == 2)
{
return state;
}
return solve();
}
virtual int solve()
{
return step();
}
};
struct VerboseSolver : public Solver
{
using Base = Solver;
int solve() override
{
std::println("Current state: {}", state);
return Base::solve();
}
};
struct ExtraVerboseSolver : public VerboseSolver
{
using Base = VerboseSolver;
int solve() override
{
std::println("Remaining iterations (prediction): {}", 3 - state);
return Base::solve();
}
};
template <typename SolverT>
void example()
{
SolverT solver{};
auto result = solver.solve();
std::println("Result: {}", result);
std::println("");
}
int main()
{
std::println("Example: Solver");
example<Solver>();
std::println("Example: VerboseSolver");
example<VerboseSolver>();
std::println("Example: ExtraVerboseSolver");
example<ExtraVerboseSolver>();
}
```
</details>
<details>
<summary>Expected outcome:</summary>
```
Example: Solver
Result: 3
Example: VerboseSolver
Current state: 0
Current state: 1
Current state: 2
Result: 3
Example: ExtraVerboseSolver
Remaining iterations (prediction): 3
Current state: 0
Remaining iterations (prediction): 2
Current state: 1
Remaining iterations (prediction): 1
Current state: 2
Result: 3
```
</details>
[Example on Godbolt.](https://godbolt.org/z/nsh9d6dT9) All three major compilers agree on the visible behaviour.
I've tried expressing this behaviour with "deducing this" feature to avoid virtual functions and maintain cooperative nature of all these classes.
<details>
<summary>Code:</summary>
```c++
#include <print>
struct Solver
{
int state = 0;
template <typename Self>
int step(this Self&& self)
{
if (self.state++ == 2)
{
return self.state;
}
return self.solve();
}
template <typename Self>
int solve(this Self&& self)
{
return self.step();
}
};
struct VerboseSolver : public Solver
{
using Base = Solver;
template <typename Self>
int solve(this Self&& self)
{
std::println("Current state: {}", self.state);
return self.Base::solve();
}
};
struct ExtraVerboseSolver : public VerboseSolver
{
using Base = VerboseSolver;
template <typename Self>
int solve(this Self&& self)
{
std::println("Remaining iterations (prediction): {}", 3 - self.state);
return self.Base::solve();
}
};
template <typename SolverT>
void example()
{
SolverT solver{};
auto result = solver.solve();
std::println("Result: {}", result);
std::println("");
}
int main()
{
std::println("Example: Solver");
example<Solver>();
std::println("Example: VerboseSolver");
example<VerboseSolver>();
std::println("Example: ExtraVerboseSolver");
example<ExtraVerboseSolver>();
}
```
</details>
The issue arises when I want to access base class function by referring through type alias for that base class (`self.Base::solve()`). In `ExtraVerboseSolver` example it seems that `Base` type alias visible in `VerboseSolver::solve` is one defined in `ExtraVerboseSolver` which leads to infinite recursion `VerboseSolver::solve -> VerboseSolver::solve`. As far as I can guess it looks like that `Base` alias in `VerboseSolver` is shadowed by `Base` alias in `ExtraVerboseSolver` as if "deducing this" contributed to type alias resolution as well. It can be mitigated by using base class name instead of a type alias. However GCC doesn't require that mitigation and behaviour follows my virtual functions example. Example on Godbolt for all three major compilers with and without type alias: https://godbolt.org/z/4fYK8s344
Unfortunately I don't know what's standard compliant behaviour hence the question in the title. Is GCC right in this case?
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs