Question Details

No question body available.

Tags

c++ clang language-lawyer ctad

Answers (6)

Accepted Answer Available
Accepted Answer
February 6, 2026 Score: 4 Rep: 20,233 Quality: Expert Completeness: 80%

In this case, the problem is actually with the use of base in the constructor:

template requires (base::enable) fnref(F* f){}

The deduction guide generated from this constructor is somewhat akin to:

template requires (base::enable) fn
ref(F f) -> fn_ref;

Clang correctly deduces that the parameter pack S can only be the empty pack, but it seems to believe that the definition of fn_ref is needed to resolve base (effectively treating base as the qualified name fn_ref::base).

All other compilers can find that base is fn_ref_call without instantiating fn_ref. And Clang is able to do so when dealing with names outside the requires-clause.

I can't find any normative wording about whether the class template should be instantiated in this case, but the standard does have an example which shows that a member alias template is not treated as a qualified name during CTAD ([over.match.class.deduct]):

template struct B { template using TA = T; template B(U, TA); };

B b{(int)0, (char)0}; // OK, deduces B

So I believe that Clang's behavior is incorrect.

Workaround: replace base with the type that it aliases:

template requires (fn_ref_call::enable) fn_ref(F
f){}
February 6, 2026 Score: 4 Rep: 48,980 Quality: Medium Completeness: 70%

Is clang correct here?

This is a confirmed clang bug specific to using concept. Clang starts accepting the program if you use the old SFINAE technique as shown below.

Is there a workaround?

Yes, you can use SFINAE:

template 
struct fnref : private fnrefcall {
private:
    using base = fnrefcall;

public: //-------------------vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv template fnref(F* f){} };

Working demo

February 6, 2026 Score: 3 Rep: 229,444 Quality: Medium Completeness: 70%

Is there a workaround?

fnrefcall seems to just be a poor std::isfunction.

So you might get rid of extra indirection and just have

template 
requires (std::isfunctionv)
struct fnref
{
public:
    fnref(F*){}
};

No extra CTAD, as default one is correct.

Demo

February 6, 2026 Score: 2 Rep: 33,791 Quality: Medium Completeness: 100%

Is clang correct here?

The issue is how CTAD handles base class constraints during template instantiation:

  • Clang's position: During CTAD, it instantiates fnref to check constructor constraints, which requires instantiating base fnrefcall. Since the primary template is undefined, it fails—even though a matching specialization exists.

  • GCC/MSVC's position: They appear to defer constraint checking or handle base class lookup differently, successfully finding the specialization.

Verdict: Likely a Clang bug or overly strict interpretation. The specialization clearly matches R(Args...), and two major compilers accept it.


Is there a workaround?

One workaround is to use, a concept instead of base class member: (Demo)

template  concept validfnrefcall = requires { 
     requires fnrefcall::enable; 
};

template struct fnref : private fnrefcall { template requires validfnrefcall fn_ref(F* ){} };
February 6, 2026 Score: 2 Rep: 14,441 Quality: Medium Completeness: 80%

A workaround suggested by Stephan T Lavavej on MSVC STL Discord (message): avoid variadic S:

#include 

template struct fnrefcall;

template struct fnrefcall { static constexpr bool enable = true; };

template struct fnref : private fnrefcall { private: using base = fnrefcall;

public: template requires (base::enable) fnref(F f){} };

template requires std::is_function_v fn_ref(F ) -> fnref;

int fn(int) { return 3; }

int main() { fnref f{&fn}; }

https://godbolt.org/z/WM71Pj7dx

I'm gonna use that, as it is the easiest one to adapt to the full code.

February 6, 2026 Score: 2 Rep: 14,441 Quality: Low Completeness: 70%

Workaround

#include 

template struct fnrefcall;

template struct fnrefcall { static constexpr bool enable = true; };

template struct fnref : private fnrefcall { private: using base = fnrefcall;

public: template requires (fnref::base::enable) // ^^^^^^^^^ fnref(F* f){} };

template requires std::isfunctionv fnref(F *) -> fnref;

int fn(int) { return 3; }

int main() { fnref f{&fn}; }

https://godbolt.org/z/3nrr4936Y