CodeNebula
C++17stladvanced

std::is_aggregate

C++17 · Practical Modern · Compiler flag: -std=c++17

Explanation

A type trait that checks whether a type is an aggregate (an array or a class with no user-declared constructors, no private/protected non-static data members, no virtual functions, and no virtual base classes). Useful for enabling aggregate-specific initialization logic in generic code.

Real-World Scenario

Dispatch to aggregate initialization or constructor-based initialization in a generic deserialization framework.

Quick Snippet

snippet.cpp
#include <type_traits>

struct Agg { int x; double y; };
class NonAgg { int x; public: NonAgg(int v) : x(v) {} };

static_assert(std::is_aggregate_v<Agg>);
static_assert(!std::is_aggregate_v<NonAgg>);

Full Example

is_aggregate.cpp
// CodeNebula - C++17 Example: std::is_aggregate
// Compile: g++ -std=c++17 -Wall -Wextra -o example is_aggregate.cpp

// Demonstrates std::is_aggregate, a C++17 type trait that detects whether
// a type is an aggregate. Aggregates support brace-initialization without
// a user-provided constructor. This trait is useful for generic code that
// needs to choose between aggregate init and constructor-based init.

#include <iostream>
#include <type_traits>
#include <string>
#include <array>
#include <vector>

// An aggregate: no user-provided constructors, no private members,
// no virtual functions, no base classes (relaxed in C++17)
struct Aggregate {
    int x;
    double y;
    std::string name;
};

// Aggregate with nested aggregate
struct Nested {
    Aggregate inner;
    int z;
};

// NOT aggregate: has user-provided constructor
struct WithConstructor {
    int x;
    WithConstructor(int v) : x(v) {}
};

// NOT aggregate: has private members
class PrivateMembers {
    int x;
public:
    int getX() const { return x; }
};

// NOT aggregate: has virtual function
struct WithVirtual {
    int x;
    virtual void foo() {}
};

// C++17: aggregate with public base class (new in C++17!)
struct Base { int a; };
struct DerivedAggregate : Base {
    int b;
};

// C-style array is an aggregate
using IntArray = int[5];

int main()
{
    // ------------------------------------------------------------------
    // 1. Aggregate types
    // ------------------------------------------------------------------
    std::cout << "=== Aggregate Types ===" << std::endl;
    std::cout << "  Aggregate        : " << std::is_aggregate_v<Aggregate> << std::endl;
    std::cout << "  Nested           : " << std::is_aggregate_v<Nested> << std::endl;
    std::cout << "  DerivedAggregate : " << std::is_aggregate_v<DerivedAggregate> << std::endl;
    std::cout << "  int[5]           : " << std::is_aggregate_v<IntArray> << std::endl;
    std::cout << "  std::array<int,3>: " << std::is_aggregate_v<std::array<int,3>> << std::endl;

    // ------------------------------------------------------------------
    // 2. Non-aggregate types
    // ------------------------------------------------------------------
    std::cout << "\n=== Non-Aggregate Types ===" << std::endl;
    std::cout << "  WithConstructor  : " << std::is_aggregate_v<WithConstructor> << std::endl;
    std::cout << "  PrivateMembers   : " << std::is_aggregate_v<PrivateMembers> << std::endl;
    std::cout << "  WithVirtual      : " << std::is_aggregate_v<WithVirtual> << std::endl;
    std::cout << "  std::string      : " << std::is_aggregate_v<std::string> << std::endl;
    std::cout << "  std::vector<int> : " << std::is_aggregate_v<std::vector<int>> << std::endl;

    // ------------------------------------------------------------------
    // 3. Fundamental types (not aggregates)
    // ------------------------------------------------------------------
    std::cout << "\n=== Fundamental Types ===" << std::endl;
    std::cout << "  int              : " << std::is_aggregate_v<int> << std::endl;
    std::cout << "  double           : " << std::is_aggregate_v<double> << std::endl;

    // ------------------------------------------------------------------
    // 4. Practical: conditional initialization
    // ------------------------------------------------------------------
    std::cout << "\n=== Practical: Aggregate Init ===" << std::endl;

    // C++17 allows aggregate init of DerivedAggregate
    DerivedAggregate da = {{10}, 20};
    std::cout << "  DerivedAggregate{10, 20}: a=" << da.a << " b=" << da.b << std::endl;

    Aggregate agg = {42, 3.14, "test"};
    std::cout << "  Aggregate{42, 3.14, test}: x=" << agg.x
              << " y=" << agg.y << " name=" << agg.name << std::endl;

    return 0;
}

/*
Expected output:

=== Aggregate Types ===
  Aggregate        : 1
  Nested           : 1
  DerivedAggregate : 1
  int[5]           : 1
  std::array<int,3>: 1

=== Non-Aggregate Types ===
  WithConstructor  : 0
  PrivateMembers   : 0
  WithVirtual      : 0
  std::string      : 0
  std::vector<int> : 0

=== Fundamental Types ===
  int              : 0
  double           : 0

=== Practical: Aggregate Init ===
  DerivedAggregate{10, 20}: a=10 b=20
  Aggregate{42, 3.14, test}: x=42 y=3.14 name=test
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example is_aggregate.cpp