Skip to content

CWG3226 [temp.res.general] It seems wrong to restrict IFNDR when no valid specialization can be generated to when "the innermost enclosing template is not instantiated" #973

Description

@HolyBlackCat

Full name of submitter (unless configured in github; will be published with the issue): Egor Mikhailov

Reference (section label): [temp.res.general]/6.1

Link to reflector thread (if any): none

Issue description:

[temp.res.general]/6.1

The program is ill-formed, no diagnostic required, if

(6.1) no valid specialization, ignoring static_assert-declarations that fail ([dcl.pre]), can be generated for a templated entity or a substatement of a constexpr if statement ([stmt.if]) within a templated entity and the innermost enclosing template is not instantiated, or

The intent of "and the innermost enclosing template is not instantiated" seems to be to remove the IFNDR status if an invalid specialization is actually produced, which in turn makes the program just IF. But the way it's currently worded is problematic.

Example 1:

template <typename T>
struct Foo
{
    Foo() {}
    Foo(T)
    {
        this->bar(); // IFNDR here?
    }
};

Foo<int> f;

Both GCC and Clang treat this->bar(); as IFNDR, which matches the intent. But since I have Foo<int> x;, which causes an instantiation of the innermost enclosing template (which is Foo), under the current wording the program turns from IFNDR to well-formed, which I guess isn't the intent.

Similarly, example 2:

template <typename T>
void a()
{
    if constexpr (sizeof(T) == 42)
    {
        int *a = 1; // IFNDR here?
    }
}

int main()
{
    a<int>();
}

I'm sure the intent is for this to be IFNDR, but since I have an instantiation of a<int>(), this is now well-formed.

Suggested resolution:

-and the innermost enclosing template is not instantiated
+and that templated entity or substatement respectively is not instantiated

Or split the sentence to something like

+no valid specialization, ignoring static_assert-declarations that fail ([dcl.pre]), can be generated for a templated entity and that entity is not instantiated, or for a substatement of a constexpr if statement ([stmt.if]) within a templated entity and that substatement is not instantiated, or

Activity

  1. justs0cold commented on Aug 29, 2026

    @justs0cold

    I believe this isn't IFNDR, but rather a hard compiler error. Because this points to the class template Foo<T>, the expression this->bar() depends on the template parameter.

    Under C++ template two-phase lookup rules, name lookup for a member of a dependent expression (this->bar()) is deferred until the template is instantiated (in this case it is Foo<int>).

    When Foo<int> is instantiated, the compiler performs the lookup for bar inside Foo<int>. Since bar is never declared anywhere in Foo, the lookup fails, and the compiler must emit a diagnostic error message.

    On x86-64 GCC 16.2:

    <source>: In constructor 'Foo<T>::Foo(T)':
    <source>:7:15: error: 'struct Foo<T>' has no member named 'bar' [-Wtemplate-body]
        7 |         this->bar();
           |               ^~~
    
  2. HolyBlackCat commented on Aug 29, 2026

    @HolyBlackCat
    Author

    @justs0cold Foo<int> f; doesn't instantiate the definition of the constructor Foo(T), only its declaration, so it can't cause this error. The error you're seeing is reported by GCC when first seeing the template, not when instantiating it (as you can see here by commenting out Foo<int> f;). GCC sees it as IFNDR under the "no valid specialization can be generated" rule.

    My point is that the wording of "no valid specialization" rule is defective, because the wording says it becomes well-formed once I instantiate the enclosing class Foo<int>, even if I don't instantiate the offending constructor.

  3. justs0cold commented on Aug 29, 2026

    @justs0cold

    @HolyBlackCat Ah, you are completely correct. If I'm not wrong, GCC 15 and 16 introduced early diagnostic checks under the flag -Wtemplate-body, which analyzes the primary template definition, sees Foo<T> lacks a bar member for any generic type, and throws an error during phase 1.

    Apparently this:

    template <typename T>
    struct Foo
    {
        Foo() {}
        Foo(T)
        {
            this->bar();
        }
    };

    can compile up to GCC 14.4.

  4. jensmaurer commented on Aug 30, 2026

    @jensmaurer
    Member
  5. changed the title [-][temp.res.general] It seems wrong to restrict IFNDR when no valid specialization can be generated to when "the innermost enclosing template is not instantiated"[/-] [+]CWG3226 [temp.res.general] It seems wrong to restrict IFNDR when no valid specialization can be generated to when "the innermost enclosing template is not instantiated"[/+] on Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions