自C++98起就有效的构造,在C++20与 C++23之间的行为是否存在差异?

编程语言 2026-07-10

也只是为了好玩,我在尝试编写一个程序,完全基于各标准之间的语法怪癖来检测C++的版本。我已经凑齐了除最新标准之外的所有解决方案。

#include <iostream>
#include <string>

#define u8 "\x01"
#define M(x, ...) __VA_ARGS__

struct B {};

struct A
{
    bool operator==(B const&);
};

bool A::operator ==(B const&) {
    return false;
}

bool operator==(B const&, A const&) {
    return true;
}

int main() {
    int m[] = { M(1'2, 3'4, 5) };
    B b;
    A a;
    // C++98's preprocessor substitutes the u8 we #define'd;
    // later standards see it as starting a Unicode string.
    // https://stackoverflow.com/a/6402166/29080386
    if (u8"\0"[0]) {
    std::cout << "C++98" << std::endl;
    // C++14 introduced the ' character as a digit separator;
    // earlier standards interpret it as a character literal
    // regardless of context.
    // https://stackoverflow.com/a/23980931/29080386
    } else if (m[0] == 5) {
        std::cout << "C++11" << std::endl;
    // C++14 was the last to support trigraphs.
    // It will interpret ??= as "#"; later standards see '?'.
    } else if ("??="[0] == '#') {
        std::cout << "C++14" << std::endl;
    // C++17 calls the global function; C++20 calls the member function.
    // https://stackoverflow.com/q/64130311/29080386
    } else if (b == a) {
        std::cout << "C++17" << std::endl;
    } else {
        std::cout << "C++20" << std::endl;
    }
    return 0;
}

我还没能在C++23中发现这样的、既有效又会破坏向后兼容性的改动。有吗?

解决方案

P2128R6,多维下标运算符(operator[] 带有多个参数):

struct Index {
    char operator[](int);

    template<typename T>
    static char(& test(int))[sizeof((*static_cast<T*>(0))[0, 0])];

    template<typename T>
    static char(& test(long))[2];

    enum { is_cxx23 = sizeof(test<Index>(0)) == 2 };
};

你可以用它来区分C++20与 C++23(is_cxx23 仅在C++23及更高版本为真,在C++98与 C++20之间为假): https://godbolt.org/z/hPnacEqa7

    } else if (b == a) {
        std::cout << "C++17" << std::endl;
    } else if (!Index::is_cxx23) {
        std::cout << "C++20" << std::endl;
    } else {
        std::cout << "C++23" << std::endl;
    }

以前,t[0, 0] 是一个逗号表达式,与 t[(0, 0)] 相同,其中左侧的 0 会被丢弃,变成 t[0]。自C++23以来,它尝试调用一个接受两个参数的 operator[] 的重载,虽然这个重载并不存在,但可以通过SFINAE来进行检查。


附注:你可以在编译时完成所有这些检查(纯粹基于语法,而不是在运行时检查任何内容): https://godbolt.org/z/nf77zfKv1

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章