34

Removing const from line 12 prevents the class What from being instantiated during compilation. I do not expect What to ever be instantiated, regardless of constness in the declaration. This is consistent between clang, gcc and MSVC so I assume it is standard. Marking the constructor explicit also does not prevent the instantiation. What am I not understanding here? Why does constness make a difference?

template <typename T> constexpr bool just_false() { return false; }

template<typename T>
class What {
    static_assert(just_false<T>(), "Why was this class instantiated?");
};

struct The {};

struct Heck {
    Heck(The) {}
    Heck(const What<int>&); // Removing 'const' from this line stops 'What<int>' from being instantiated
};

int main() {
    The the;
    Heck{the};
}

The just_false incantation is just to prevent the static assert from always triggering regardless of instantiation.

Compiler explorer link: https://godbolt.org/z/8cETcfss5

Martin Ba
  • 37,187
  • 33
  • 183
  • 337
Dave Magnets
  • 343
  • 2
  • 6
  • 7
    Note [\[temp.inst\]/9](https://timsong-cpp.github.io/cppwp/n4861/temp.inst#9) says "If the function selected by overload resolution can be determined without instantiating a class template definition, it is unspecified whether that instantiation actually takes place." So it's nice of the compilers to skip the instantiation when the `const` is absent, but even in that case they're not required to, and the program could still fail to compile. – aschepler Jan 18 '23 at 14:52

3 Answers3

33

If const is there, and What<int> happens to have a constructor taking The, that would allow Heck(const What<int> &) to be used (because a const reference can bind to a temporary produced by such constructor). Checking for that constructor of What<int> requires instantiating the What<int> template.

If there is no const, no implementation of What<int> could possibly make Heck(What<int> &); be called, so there's no point in instantiating it.


But it seems that no matter what constructor What<int> has, the Heck(The) overload would take precedence, so strictly speaking, this instantiation seems to be unnecessary in this specific case.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • 3
    @DaveMagnets A non-const lvalue reference can't bind to a temporary. A constructor of `What` we're looking for would produce a temporary, which we couldn't bind to the reference. – HolyBlackCat Jan 18 '23 at 06:47
  • 1
    I was confused by your answer until I realized that when you said "instantiating `What`", you meant instantiating the template, not instantiating the class. -- Obvious in retrospect, but confusing on first read. – R.M. Jan 19 '23 at 13:36
  • 2
    @R.M. "Instantiating" always refers to templates. For an object, you'd say "create an instance of ...", or "create an object/variable of type ...". – HolyBlackCat Jan 19 '23 at 13:37
  • 3
    I apologize if I'm not 100% up to speed on the minutiae of "official" C++ terminology. I was simply pointing out that (even if it's technically "incorrect" for C++, if not necessarily other languages), "instantiate" is commonly used by many people to mean "create an instance of a class", so you could make your answer clearer to more readers by editing it. Perhaps by changing it to "_Checking for that constructor of What requires instantiating the What template." or similar. A minor edit can greatly help clarity. – R.M. Jan 19 '23 at 16:12
8

If What had a constructor such as What::What(The), then the implicit conversion Heck(What(the)) would be valid for Heck::Heck(const What&) but not for Heck::Heck(What&).

During overload resolution for the call Heck(the), the search for viable functions eliminates Heck::Heck(What&) but does not eliminate Heck::Heck(const What&), which gets carried over to the search for a best viable function, which requires instantiating What.

Etienne Laurin
  • 6,731
  • 2
  • 27
  • 31
  • 3
    It can't be called with an rvalue. cppreference says "For every argument there must be at least one implicit conversion sequence that converts it to the corresponding parameter" – Etienne Laurin Jan 18 '23 at 06:49
-2

Will this stop the error message?

struct Heck {
    Heck(The const&) {}; // Removing 'const' from this line causes 'What<int>' to be instantiated
    Heck(const What<int>&);
};

This is supposed to prevent implicit conversion/copy constructor call.

Red.Wave
  • 2,790
  • 11
  • 17