4

Consider the following example:

template <class T>
  class C
{
  public:
    C();
    C(C&& rhs);
  private:
    T m_data;
};

template <class T>
C<T>::C()
: m_data(T())
{
}

template <class T>
C<T>::C(C&& rhs)
: m_data(rhs.data)
{
}

int main()
{
    C<int> i;
}

Line : m_data(rhs.data) contains an error as C does not have a member named data. But none of the compilers that I tried (gcc 5.2, clang 3.5.1) detected that error.

But when I add the following line to main function the compiler detects the error:

C<int> j = std::move(i);

Why the compiler does not give an error in the first case?

Even if that particular function is not called it can figure out that C has no member named data.

In addition when I change the definition of move constructor to the following:

template <class T>
C<T>::C(C&& rhs)
: m_data(rhs.data)
{
  data = 0;
}

the compiler gives error on line data = 0; but not on : m_data(rhs.data). So the function gets parsed.

Hrant
  • 496
  • 1
  • 5
  • 13
  • 3
    Template code will be generated only if explicitly used. In the statement you added in the main, you tell the compiler the use to move constructer. – KnightsWatch Sep 03 '15 at 11:29

4 Answers4

8

There is 2 passes to check error in template.

  • One for non dependent code
  • One for dependent code (done at instantiation)

Your code is template dependent, so it is checked only when the method is instantiated.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
5

Your template is ill-formed but the error does not require a diagnostic (in other words, the compiler is allowed to not give an error message and can do anything it wants). More precisely, the Standard says

Similarly, if the id-expression in a class member access expression for which the type of the object expression is the current instantiation does not refer to a member of the current instantiation or a member of an unknown specialization, the program is ill-formed even if the template containing the member access expression is not instantiated; no diagnostic required.

In your code C is the current instantiation and rhs.data is a class member access expression but does not refer to a member of the current instantiation and not to a member of an unknown specialization (which would be the case if C had dependent base classes, i.e if you would have written class C : T or something similar).

To read up on these rules, see this answer . It is also worth nothing that this kind of code has always been ill-formed (no diagnostic required), even in C++03 that didn't have this addition rule that I quoted. Because this code makes the template have no valid instantiation, for all possible types of T. But the existing rule of C++03 is rather broad and this addition of C++11 is a succinct test that allows this kind of code to be safely rejected.

Community
  • 1
  • 1
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
4

the compiler does not try to compile C(C&& rhs); since you don't call it (in the first try). this is called the zero overhead rule in C++ - you don't pay on what you don't use.

when you try to call it the compiler than tries to compile the function and fails.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • even if I don't use it the compiler should compile the code – Hrant Sep 03 '15 at 11:28
  • if that was the case, each EXE aould weight few dozens megabytes for the simplest "hello world" program. it would try to compile in all the C and C++ libraries.. – David Haim Sep 03 '15 at 11:29
  • I meant it should check that the C++ code is correct, not generate machine code for that function. – Hrant Sep 03 '15 at 11:33
  • but you clearly see that isn't the case. if your template is not being called your compiler will not try to parse it. as long as the syntax is not broken (missing `;`,`}` etc.) the compiler will not mind – David Haim Sep 03 '15 at 11:33
  • 1
    @Hrant It´s impossible to do that, because if it is an error or not depends on the type, and there are infinite many types (custom classes etc.etc.), so it´s not done at all. – deviantfan Sep 03 '15 at 11:50
3

In this case, the compiler obiously couldn't figure out that the move constructor will never work. It is allowed to do so, but not required.

In the general case, it is hard to detect that there is no T whatsoever for which the code could be compiled. If it just fails for C<int> and C<float>, but works for C<my_class>, the compiler must not complain (as long as the function isn't used for int or float).

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
Bo Persson
  • 90,663
  • 31
  • 146
  • 203