c++g++clangapple-clang

Apple Clang default constructor for structs


typedef struct {
    int x;
    int y;
} Test;

int main() {
    // This doesn't compile with Apple Clang
    Test test1 = Test(10, 5);

    // But anyway I can do this
    Test test2 = { .x = 10, .y = 5 };

    // But this also doesn't compile with Apple Clang
    Test *test3 = new Test(10, 5);
    return 0;
}

This code work on my Ubuntu which use G++ 13.2.0.

Trying to compile it on my Mac which use Apple Clang 15.0.0 get me this error:

no matching constructor for initialization of 'Test'

candidate constructor (the implicit copy constructor) not viable: requires 1 argument

candidate constructor (the implicit move constructor) not viable: requires 1 argument

candidate constructor (the implicit default constructor) not viable: requires 0 arguments

I suppose G++ generate a default constructor for structs which take as many parameters as the struct has attributes, but Apple Clang doesn't? Why isn't it standard? Is there a "one line" way to alloc on the heap and init my struct? What should I do to make my code more portable?

CMakeLists: (minimal one for this code snippet, new project generated by CLion)

cmake_minimum_required(VERSION 3.29)
project(untitled)

set(CMAKE_CXX_STANDARD 26)

add_executable(untitled main.cpp)

Solution

  • Aggregate initialization with parentheses is a C++20 feature. You need to enable C++20 for it to work and have a recent enough compiler to support it. (This has been implemented in Clang relatively late. Unfortunately the versioning used by Apple doesn't match upstream versioning, so it is a bit hard to tell whether your version supports it.)

    You can use aggregate initialization with braces in earlier C++ editions:

    Test test1 = Test{10, 5};
    
    Test *test3 = new Test{10, 5};
    

    Aggregate initialization with designated initializers (Test test2 = { .x = 10, .y = 5 };) is also a C++20 feature, but GCC and Clang supported it as a GNU extension already before that when not compiling in strictly standard conforming mode.


    Also, as a side note, none of this has anything to do with default constructors. A default constructor is a constructor which can be called without any arguments. You are giving two arguments here.

    A default constructor is implicitly generated (as indicated by the error message), but not viable, because it would require no arguments.

    No implicit constructor taking parameters corresponding to the members is ever generated implicitly.

    You are not using any constructor at all here. Instead you are relying on aggregate initialization which directly initializes the members of an aggregate class from the initializers, rather than going through a constructor call. This is (a generalization of) the aggregate initialization behavior for structs in C, where constructors do not exist.


    Also,

    typedef struct {
        int x;
        int y;
    } Test;
    

    should be

    struct Test {
        int x;
        int y;
    };
    

    instead. The two behave practically identical in C++ (but not C). The former is a C pattern that is very non-idiomatic in C++. C++ intentionally changed the semantics of the latter relative to C, so that the typedefs wouldn't be needed anymore.