2

A constructor fails to quality as constexpr if the class contains std::vector as its data member since std::vector doesn't have constexpr constructor (atleast until C++17 standards, for C++20 I came across this draft). The issue is if the class itself is templatized, the code successfully gets compiled despite the fact that it has constexpr constructor and std::vector as data member.

Consider following two code snippets:

#include <vector>

class Y {
public:
  constexpr Y(){}
  std::vector<int> v{};
};

int main() {
  Y ob;
  return 0;
}

This code fails to compile whereas the following one compiles successfully

#include <vector>

template<typename T>
class X{
public: 
  constexpr X(){}
  std::vector<T> v;
};

int main() {
  X<int> ob;
  return 0;
}

I do have a slight feel that this might have to do something with how template class is compiled since when I declare a constexpr instance of X constexpr X<int> ob; , it fails to compile but unable to figure out what's actually going under the hood.

Edit: I am using GCC 8.3.1 . Following is the error message I got when compiling the first code snippet with C++17

error: call to non-‘constexpr’ function ‘std::vector<_Tp, _Alloc>::vector() [with _Tp = int; _Alloc = std::allocator<int>]’
   constexpr Y(){}
  • How does it fail to compile? Really silently, so you could not post the error? What compiler do you use? – 273K May 28 '23 at 00:46
  • 1
    According to my tests, the first program compiles with gcc and clang using the language C++20, but fails with C++17 and earlier. The second program compiles in all language versions. These are the error messages: [clang](https://godbolt.org/z/jMq38q5qs) [gcc](https://godbolt.org/z/d1rWGjbGd) – Andreas Wenzel May 28 '23 at 00:49
  • Aren't you using C++20 notation in the first example? – tadman May 28 '23 at 00:56
  • @tadman What C++20 notation? – Nathan Pierson May 28 '23 at 01:44
  • 1
    The second example IIRC is ill-formed; No diagnostic required. It "works" because you don't use it in a constexpr context. – NathanOliver May 28 '23 at 01:46
  • Also note std::vector isn't constexpr until C++20 – Pepijn Kramer May 28 '23 at 05:51
  • In the first code snippet the compiler can easily determine that the constructor isn't `constexpr`, but in the second one it's harder to prove that there's no `T` such that the constructor is `constexpr`. (The compiler would need to go through all the possible specializations of `std::vector` for this to happen and templates from the standard library are not treated differently to other templates so in general there could be arbitrarily many specializations and available specializations, not to mention more than one template (parameter) could be involved) – fabian May 28 '23 at 06:59

1 Answers1

0

Before C++23 declaring a function (template) as constexpr which can never actually be called as part of a constant expression with any function or template arguments makes the program ill-formed, no diagnostic required (IFNDR). In other words the compiler is allowed to notice that the constexpr can never actually work and so may refuse to compile the program. I guess this was intended so that the compiler can tell the user about mistakes in writing functions that should be usable in constant expressions as early as possible. This was changed only recently for C++23 with P2448.

Before C++20, the default constructor for std::vector is not constexpr, but your constructor constexpr X(){} will use it. Therefore your constructor cannot ever be called as part of a constant expression evaluation and therefore the program is IFNDR. This applies to both examples.

However, because it is IFNDR, not just ill-formed, it is unspecified whether or not the program will compile. In fact IFNDR is essentially equivalent to undefined behavior for all inputs. The compiler could in theory do whatever it wants with the program, there are no standard-imposed requirements.

In fact recognizing that std::vector<T>'s default constructor is never constexpr when T is a template parameter is basically impossible. There could be any number of partial and explicit specializations of std::vector that declare the default constructor differently. The only thing that actually guarantees that none of them are declared constexpr is that the standard (before C++20) imposed that requirement.

Since C++20, the default constructor of std::vector is constexpr and so the code is well-formed and should compile in both examples.

And starting with C++23 it shouldn't matter whether or not the default constructor of the member is constexpr.

user17732522
  • 53,019
  • 2
  • 56
  • 105