1

I very rarely use C-arrays myself but I came up with the following bit of code while trying to answer someone else's question:

#include <cstddef>

template <std::size_t n>
class A {
public:
  explicit A(const int (&arr) [n]) : arr_(arr) {  }

private:
  const int arr_ [n];
};

int main(int, char**) {
  const int arr [3] = { 1, 2, 3 };
  A<3> a (arr); // (1)

  return 0;
}

It doesn't compile and I don't expect it to:

<source>:6:38: error: array initializer must be an initializer list
  explicit A(const int (&arr) [n]) : arr_(arr) {  }
                                     ^
<source>:14:8: note: in instantiation of member function 'A<3>::A' requested here
  A<3> a (arr);

I don't expect it to compile for the same reason the following doesn't compile:

int main(int, char**) {
  const int arr_a [3] = { 1, 2, 3 };
  const int arr_b [3] = arr_a;

  return 0;
}

Here's the compilation error:

<source>:3:13: error: array initializer must be an initializer list
  const int arr_b [3] = arr_a;
            ^

But if you in the first code block comment out the line marked (1) it compiles. As if, as I understand it, there's some way to call A::A(const int (&) [n]) that's valid. In my mind, even trying to initialise arr_ with arr is wrong.

Why does the first code block compile if you comment out the line marked (1)? Is there a way to call A::A(const int (&) [n]) that's valid?

  • 2
    Are you asking why does `const int arr [3] = { 1, 2, 3 };` compile? – AndyG Mar 16 '18 at 09:48
  • No, I edited my question to try to clarify. I'm asking why in `A`'s constructor `arr_(arr)` compiles. In my mind, even trying to initialise `arr_` with `arr` is wrong. –  Mar 16 '18 at 09:50
  • why don't you use `memcpy` for copying the arrays. – Mohit Mar 16 '18 at 09:51
  • A template is not an actual class until instantiated. It's first when you instantiate a template the class exists and can be checked for errors the same way as other classes. – Some programmer dude Mar 16 '18 at 09:52
  • 5
    Maybe there's a better dupe, but [Why do template classes allows functions which cannot compile?](//stackoverflow.com/q/31328754) is relevant. – Baum mit Augen Mar 16 '18 at 09:53
  • @Mohit They should *not* use `memcpy`. `std::copy`, if they have to use C arrays, or better `std::array`. – Baum mit Augen Mar 16 '18 at 09:53
  • @Mohit, I'm not looking for a way to copy a C-array. –  Mar 16 '18 at 09:58
  • @BaummitAugen, I read your link and the comment thread below, but I still don't fully understand. I clearly don't understand how compilers handle templates, I'll have to read up on it. For example, [this](https://godbolt.org/g/r92ZsB) doesn't compile, so the compiler doesn't completely ignore the definition. Is it all optional? That the compilers don't _have_ to look at the definition but they do anyway? –  Mar 16 '18 at 10:37
  • @ryhp Compilers are allowed, but not required, to check the syntax. They are, more generally, also allowed to detect any template for which no set of template arguments would yield a well-formed instantiation, which is the case in your example. – Baum mit Augen Mar 16 '18 at 10:38
  • @BaummitAugen, so it wouldn't be wrong of a compiler to successfully compile the code given that `fn`'s definition is never used? –  Mar 16 '18 at 10:45
  • @BaummitAugen I think they are required to check the syntax; the example in ryhp's previous comment must fail to compile. – M.M Mar 16 '18 at 10:46
  • @M.M Not 100% sure about that tbh, at least MSVC does not check apparently. Granted, that is rather weak proof. There is [Are C++ templates be checked for syntax errors without instantiating them?](//stackoverflow.com/q/37731401) on that topic, but the term syntax error appears to be applied rather loosely there. – Baum mit Augen Mar 16 '18 at 10:53

2 Answers2

0

It doesn't compile. Literally. You're not using the template, hence the compiler doesn't even look at it.

Max Vollmer
  • 8,412
  • 9
  • 28
  • 43
  • 2
    The compiler looks at it. You can't just write anything in there. – AndyG Mar 16 '18 at 09:53
  • 2
    @ryhp That doesn't instantiate the member function either. See my link above. – Baum mit Augen Mar 16 '18 at 09:55
  • 1
    @ryhp Implicit instantiation happen only when the function is actually used. – llllllllll Mar 16 '18 at 09:56
  • Your `A` has two constructors, one is valid, the other is not. Your example uses the valid one. – Max Vollmer Mar 16 '18 at 09:57
  • @MaxVollmer Your reason is wrong, nothing to do with optimization. – llllllllll Mar 16 '18 at 09:58
  • @ryhp Believe it or not, this compiles as well http://rextester.com/LPRW90518 (at least on VC++). – Henri Menke Mar 16 '18 at 09:58
  • @liliscent I edited my comment, but I am curious: How is not compiling an unused method not optimization? It prevents using up unnecessary space with code that never will be executed in the resulting executable. Sounds like optimization to me. – Max Vollmer Mar 16 '18 at 10:00
  • @HenriMenke That is ill formed with no diagnostics required, as is the example in OP, due to 17.6/8.1 in N4659. – Baum mit Augen Mar 16 '18 at 10:00
  • 2
    @MaxVollmer Optimizations are optional (except RVO in C++17 in some instances), not instantiating unused templates is not. Optimization does not change the validity of a program, while that rule does. – Baum mit Augen Mar 16 '18 at 10:02
  • @BaummitAugen There is no not instantiated template in the case we are talking about right now. The template is instantiated and one constructor of the resulting class is not being compiled, because it's not being used. Why is that not optimization? – Max Vollmer Mar 16 '18 at 10:05
  • 1
    @MaxVollmer The constructor itself is a template, too; and as it is not called, its definition *must* not be instantiated. That is, in general, not optional. See my link above for details. (In this pathological case, things are allowed to behave different as the code is ill-formed, no diagnostics required, because the template cannot be instantiated for any template parameter, but that's a corner-case tangential to the main point.) – Baum mit Augen Mar 16 '18 at 10:09
  • @BaummitAugen Thanks for the explanation! – Max Vollmer Mar 16 '18 at 10:16
0

The error message is actually a bit longer than you quoted. It also answers your question:

test.cc:6:38: error: array initializer must be an initializer list
  explicit A(const int (&arr) [n]) : arr_(arr) {  }
                                     ^
test.cc:14:8: note: in instantiation of member function 'A<3>::A' requested here
  A<3> a (arr); // (1)
       ^

So, only if the template is instantiated will the compiler actually attempt to compile the faulty code and discover its flaw. Hence, no error is produced if you comment out that line.

Actually, compilation of templates is a bit more complicated, being split into two phases. In the first phase, the template is essentially checked for typographical correctness, while the actual compilation into code can only be done in the second phase, when the template is instantiated.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • What exactly do you mean by _"typographical correctness"_? In my mind that means things like, instead of the keyword `for` I wrote `dor` or whatever. But [this](https://godbolt.org/g/GZouB2) doesn't compile, even though it's (in my mind) typographically correct. –  Mar 16 '18 at 11:05