使用C++时如何改善编译器错误消息。 std ::访问?

发布于 2025-02-04 13:45:19 字数 753 浏览 4 评论 0原文

我正在使用C ++ 17的std ::访问()在带有许多替代方案的变体上函数,并且每当我忘记访问者中的一个或多个替代方案时,编译器产生的错误消息非常难以理解。

例如

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

using Foo = std::variant<A, B, /* ... many more alternatives ... */>;

Foo foo;

std::visit(overloaded{
    [](A const& a) { /* ... */ },
    [](B const& b) { /* ... */ },
    /* ... forgot 1+ alternatives ... */
    }, foo
);

,在上面的代码示例中,编译器可以根据替代数量产生数千个字符的错误消息。有没有办法改进这些错误消息,以便编译器会输出以下内容?

example.cc:8-13: error: Non-exhaustive visitor -- missing alternative of type 'X'

I am using C++17's std::visit() function on a variant with many alternatives, and the error messages produced by the compiler whenever I forget one or more of the alternatives in my visitor are quite difficult to understand.

e.g.

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

using Foo = std::variant<A, B, /* ... many more alternatives ... */>;

Foo foo;

std::visit(overloaded{
    [](A const& a) { /* ... */ },
    [](B const& b) { /* ... */ },
    /* ... forgot 1+ alternatives ... */
    }, foo
);

In the above code example, the compiler can produce error messages that are thousands of characters in length, depending on the number of alternatives. Is there a way to improve these error messages so that the compiler will output something like the following instead?

example.cc:8-13: error: Non-exhaustive visitor -- missing alternative of type 'X'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

他是夢罘是命 2025-02-11 13:45:20

我第一次尝试解决此问题的尝试在这里。经过一些谷歌搜索和大量的反复试验之后,我想出了一个更好的解决方案,我发布了在这里。为了方便起见,我将在下面复制解决方案。


这是概念证明。

#include <iostream>
#include <variant>


template <typename> class Test { };

using Foo = std::variant<
    Test<struct A>,
    Test<struct B>,
    Test<struct C>,
    Test<struct D>
    >;

using Bar = std::variant<
    Test<struct E>,
    Test<struct F>,
    Test<struct G>,
    Test<struct H>,
    Test<struct I>,
    Test<struct J>,
    Test<struct K>,
    Test<struct L>
    >;


template <typename T>
struct DefineVirtualFunctor
{
    virtual int operator()(T const&) const = 0;
};

template <template <typename> typename Modifier, typename... Rest>
struct ForEach { };
template <template <typename> typename Modifier, typename T, typename... Rest>
struct ForEach<Modifier, T, Rest...> : Modifier<T>, ForEach<Modifier, Rest...> { };

template <typename Variant>
struct Visitor;
template <typename... Alts>
struct Visitor<std::variant<Alts...>> : ForEach<DefineVirtualFunctor, Alts...> { };


struct FooVisitor final : Visitor<Foo>
{
    int operator()(Test<A> const&) const override { return  0; }
    int operator()(Test<B> const&) const override { return  1; }
    int operator()(Test<C> const&) const override { return  2; }
    int operator()(Test<D> const&) const override { return  3; }
};

struct BarVisitor final : Visitor<Bar>
{
    int operator()(Test<E> const&) const override { return  4; }
    int operator()(Test<F> const&) const override { return  5; }
    int operator()(Test<G> const&) const override { return  6; }
    int operator()(Test<H> const&) const override { return  7; }
    int operator()(Test<I> const&) const override { return  8; }
    int operator()(Test<J> const&) const override { return  9; }
    int operator()(Test<K> const&) const override { return 10; }
    int operator()(Test<L> const&) const override { return 11; }
};


int main(int argc, char const* argv[])
{
    Foo foo;
    Bar bar;
    
    switch (argc) {
    case  0: foo = Foo{ std::in_place_index<0> }; break;
    case  1: foo = Foo{ std::in_place_index<1> }; break;
    case  2: foo = Foo{ std::in_place_index<2> }; break;
    default: foo = Foo{ std::in_place_index<3> }; break;
    }
    switch (argc) {
    case  0: bar = Bar{ std::in_place_index<0> }; break;
    case  1: bar = Bar{ std::in_place_index<1> }; break;
    case  2: bar = Bar{ std::in_place_index<2> }; break;
    case  3: bar = Bar{ std::in_place_index<3> }; break;
    case  4: bar = Bar{ std::in_place_index<4> }; break;
    case  5: bar = Bar{ std::in_place_index<5> }; break;
    case  6: bar = Bar{ std::in_place_index<6> }; break;
    default: bar = Bar{ std::in_place_index<7> }; break;
    }
    
    std::cout << std::visit(FooVisitor{ }, foo) << "\n";
    std::cout << std::visit(BarVisitor{ }, bar) << "\n";

    return 0;
}

如您所见,访问者类模板接受std :: variant将作为模板参数键入,从中定义了必须在任何子类中实现的接口从模板类实例化继承。如果在儿童课程中,您碰巧忘记覆盖一种纯虚拟方法,那么您将获得以下错误。

$ g++ -std=c++17 -o example example.cc
example.cc: In function ‘int main(int, const char**)’:
example.cc:87:41: error: invalid cast to abstract class type ‘BarVisitor’
   87 |     std::cout << std::visit(BarVisitor{ }, bar) << "\n";
      |                                         ^
example.cc:51:8: note:   because the following virtual functions are pure within ‘BarVisitor’:
   51 | struct BarVisitor final : Visitor<Bar>
      |        ^~~~~~~~~~
example.cc:29:17: note:     ‘int DefineVirtualFunctor<T>::operator()(const T&) const [with T = Test<J>]’
   29 |     virtual int operator()(T const&) const = 0;
      |                 ^~~~~~~~

这比编译器使用std ::访问()时通常生成的错误消息要容易得多。

My first attempt at solving this problem can be found here. After some some googling and lots of trial and error, I've come up with a much better solution, which I've posted here. I'll copy-paste the solution, below, for convenience.


Here is a proof of concept.

#include <iostream>
#include <variant>


template <typename> class Test { };

using Foo = std::variant<
    Test<struct A>,
    Test<struct B>,
    Test<struct C>,
    Test<struct D>
    >;

using Bar = std::variant<
    Test<struct E>,
    Test<struct F>,
    Test<struct G>,
    Test<struct H>,
    Test<struct I>,
    Test<struct J>,
    Test<struct K>,
    Test<struct L>
    >;


template <typename T>
struct DefineVirtualFunctor
{
    virtual int operator()(T const&) const = 0;
};

template <template <typename> typename Modifier, typename... Rest>
struct ForEach { };
template <template <typename> typename Modifier, typename T, typename... Rest>
struct ForEach<Modifier, T, Rest...> : Modifier<T>, ForEach<Modifier, Rest...> { };

template <typename Variant>
struct Visitor;
template <typename... Alts>
struct Visitor<std::variant<Alts...>> : ForEach<DefineVirtualFunctor, Alts...> { };


struct FooVisitor final : Visitor<Foo>
{
    int operator()(Test<A> const&) const override { return  0; }
    int operator()(Test<B> const&) const override { return  1; }
    int operator()(Test<C> const&) const override { return  2; }
    int operator()(Test<D> const&) const override { return  3; }
};

struct BarVisitor final : Visitor<Bar>
{
    int operator()(Test<E> const&) const override { return  4; }
    int operator()(Test<F> const&) const override { return  5; }
    int operator()(Test<G> const&) const override { return  6; }
    int operator()(Test<H> const&) const override { return  7; }
    int operator()(Test<I> const&) const override { return  8; }
    int operator()(Test<J> const&) const override { return  9; }
    int operator()(Test<K> const&) const override { return 10; }
    int operator()(Test<L> const&) const override { return 11; }
};


int main(int argc, char const* argv[])
{
    Foo foo;
    Bar bar;
    
    switch (argc) {
    case  0: foo = Foo{ std::in_place_index<0> }; break;
    case  1: foo = Foo{ std::in_place_index<1> }; break;
    case  2: foo = Foo{ std::in_place_index<2> }; break;
    default: foo = Foo{ std::in_place_index<3> }; break;
    }
    switch (argc) {
    case  0: bar = Bar{ std::in_place_index<0> }; break;
    case  1: bar = Bar{ std::in_place_index<1> }; break;
    case  2: bar = Bar{ std::in_place_index<2> }; break;
    case  3: bar = Bar{ std::in_place_index<3> }; break;
    case  4: bar = Bar{ std::in_place_index<4> }; break;
    case  5: bar = Bar{ std::in_place_index<5> }; break;
    case  6: bar = Bar{ std::in_place_index<6> }; break;
    default: bar = Bar{ std::in_place_index<7> }; break;
    }
    
    std::cout << std::visit(FooVisitor{ }, foo) << "\n";
    std::cout << std::visit(BarVisitor{ }, bar) << "\n";

    return 0;
}

As you can see, the Visitor class template accepts a std::variant type as a template parameter, from which it will define an interface that must be implemented in any child classes that inherit from the template class instantiation. If, in a child class, you happen to forget to override one of the pure virtual methods, you will get an error like the following.

$ g++ -std=c++17 -o example example.cc
example.cc: In function ‘int main(int, const char**)’:
example.cc:87:41: error: invalid cast to abstract class type ‘BarVisitor’
   87 |     std::cout << std::visit(BarVisitor{ }, bar) << "\n";
      |                                         ^
example.cc:51:8: note:   because the following virtual functions are pure within ‘BarVisitor’:
   51 | struct BarVisitor final : Visitor<Bar>
      |        ^~~~~~~~~~
example.cc:29:17: note:     ‘int DefineVirtualFunctor<T>::operator()(const T&) const [with T = Test<J>]’
   29 |     virtual int operator()(T const&) const = 0;
      |                 ^~~~~~~~

This is much easier to understand than the error messages that the compiler usually generates when using std::visit().

携余温的黄昏 2025-02-11 13:45:20

IMO您可以将过载设置包装在函数对象中,该功能对象在错过的情况下执行默认例程(就像default e节 switch> switch> switch 语句)一​​样。而且我将默认的过载放在开始时,以免忘记它:

auto any_visitor=[](auto&& val, auto&& default_fn,auto ...fn){
    overloaded vis{fn ...};
    if constexpr(std::is_invokable_v<decltype(vis), decltype(val)>)
        return vis(std::forward(val));
    else
        return std::invoke(std::forward(default_fn), std::forward(val));
};

std::visit(
    std::bind_back(
        any_visitor,
        [](auto&&){
            /* default visitor logic for missed cases*/
        },
        [](A const& a) { /* ... */ },
        [](B const& b) { /* ... */ },
        /* ... forgot 1+ alternatives ... */
    }),
    foo
);

std :: bind_back不是奇迹。它只是提供清洁的语法,而无需占位座参数:

namespace par=std::placeholders;

std::visit(
    std::bind(
        any_visitor, par::_1//keep the front parameter
        [](auto&&){
            /* default visitor logic for missed cases*/
        },
        [](A const& a) { /* ... */ },
        [](B const& b) { /* ... */ },
        /* ... forgot 1+ alternatives ... */
    }),
    foo
);

IMO you can wrap the overload set in a function object that executes a default routine on missed cases (much like a default section in a switch statement). And I put the default overload at the beginning, so as to not forget it:

auto any_visitor=[](auto&& val, auto&& default_fn,auto ...fn){
    overloaded vis{fn ...};
    if constexpr(std::is_invokable_v<decltype(vis), decltype(val)>)
        return vis(std::forward(val));
    else
        return std::invoke(std::forward(default_fn), std::forward(val));
};

std::visit(
    std::bind_back(
        any_visitor,
        [](auto&&){
            /* default visitor logic for missed cases*/
        },
        [](A const& a) { /* ... */ },
        [](B const& b) { /* ... */ },
        /* ... forgot 1+ alternatives ... */
    }),
    foo
);

The std::bind_back is not a miracle. It just provides cleaner syntax with no need for place holder parameters:

namespace par=std::placeholders;

std::visit(
    std::bind(
        any_visitor, par::_1//keep the front parameter
        [](auto&&){
            /* default visitor logic for missed cases*/
        },
        [](A const& a) { /* ... */ },
        [](B const& b) { /* ... */ },
        /* ... forgot 1+ alternatives ... */
    }),
    foo
);
彩虹直至黑白 2025-02-11 13:45:20

如果您可以使用C ++ 20,则可以使用概念。 AFAIK,改进模板代码中的错误消息是人们想要拥有此功能的关键原因之一。 “简单地”包裹std ::访问使用您自己的访问功能访问 <代码>需要子句:

#include <concepts>
#include <variant>

template <class Visitor, class... Ts>
requires (std::invocable<Visitor, Ts> && ...)
auto my_visit(Visitor&& vis, std::variant<Ts...> const& var) 
{
    return std::visit(std::forward<Visitor>(vis), var);
}

使用此包装器将Clang的错误消息从184行减少到28行。 live demo
(请注意,此实现仅涵盖const std :: variant&lt; ...&gt;&amp;一次,而其中只有一个,而std ::访问可以处理 不幸的是,它一次任意数量的变体。

它仍然不如您在问题中表达的愿望那么短,但它比原始的要好。

旁注:我不能告诉你,为什么C ++ 20's std ::访问没有此内置的子句。

If you can use C++20, you can make use of concepts. Afaik, improving error messages in templated code was one of the key reasons why people wanted to have this feature. "Simply" wrap std::visit with your own visit function and add a requires clause:

#include <concepts>
#include <variant>

template <class Visitor, class... Ts>
requires (std::invocable<Visitor, Ts> && ...)
auto my_visit(Visitor&& vis, std::variant<Ts...> const& var) 
{
    return std::visit(std::forward<Visitor>(vis), var);
}

Using this wrapper reduces clang's error message from 184 lines down to 28 lines. Live Demo
(Note that this implementation only covers const std::variant<...>& and only one of them at a time, while std::visit can handle an arbitrary number of variants at once.)

Unfortunately, it is still not as short and descriptive as the wish you expressed in your question, but it is mush better than the original, I guess.

Side note: I cannot tell you, why C++20's std::visit doesn't have this requires clause built in.

帅哥哥的热头脑 2025-02-11 13:45:20

我想出了一个不那么理想的解决方案,但总比没有好。如果最终出现了更好的解决方案,我将很乐意将接受的答案切换为此。

这是概念证明。

#include <variant>

#define STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA \
[](auto... __args) { \
    static_assert(always_false_v<decltype(__args)...>, "non-exhaustive visitor"); \
},

template <typename... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <typename... Ts> overloaded(Ts...) -> overloaded<Ts...>;

template <typename> constexpr bool always_false_v = false;

template <typename> class Test { };

using Foo = std::variant<
    std::monostate,
    Test<struct A>,
    Test<struct B>,
    Test<struct C>,
    Test<struct D>,
    Test<struct E>,
    Test<struct F>,
    Test<struct G>,
    Test<struct H>,
    Test<struct I>,
    Test<struct J>,
    Test<struct K>,
    Test<struct L>,
    Test<struct M>,
    Test<struct N>,
    Test<struct O>,
    Test<struct P>,
    Test<struct Q>,
    Test<struct R>,
    Test<struct S>,
    Test<struct T>,
    Test<struct U>,
    Test<struct V>,
    Test<struct W>,
    Test<struct X>,
    Test<struct Y>,
    Test<struct Z>
    >;

int main(int argc, char const* argv[])
{
    Foo foo;

    switch (argc) {
    case  0: foo = Foo{ std::in_place_index< 0> }; break;
    case  1: foo = Foo{ std::in_place_index< 1> }; break;
    case  2: foo = Foo{ std::in_place_index< 2> }; break;
    case  3: foo = Foo{ std::in_place_index< 3> }; break;
    case  4: foo = Foo{ std::in_place_index< 4> }; break;
    case  5: foo = Foo{ std::in_place_index< 5> }; break;
    case  6: foo = Foo{ std::in_place_index< 6> }; break;
    case  7: foo = Foo{ std::in_place_index< 7> }; break;
    case  8: foo = Foo{ std::in_place_index< 8> }; break;
    case  9: foo = Foo{ std::in_place_index< 9> }; break;
    case 10: foo = Foo{ std::in_place_index<10> }; break;
    case 11: foo = Foo{ std::in_place_index<11> }; break;
    case 12: foo = Foo{ std::in_place_index<12> }; break;
    case 13: foo = Foo{ std::in_place_index<13> }; break;
    case 14: foo = Foo{ std::in_place_index<14> }; break;
    case 15: foo = Foo{ std::in_place_index<15> }; break;
    case 16: foo = Foo{ std::in_place_index<16> }; break;
    case 17: foo = Foo{ std::in_place_index<17> }; break;
    case 18: foo = Foo{ std::in_place_index<18> }; break;
    case 19: foo = Foo{ std::in_place_index<19> }; break;
    case 20: foo = Foo{ std::in_place_index<20> }; break;
    case 21: foo = Foo{ std::in_place_index<21> }; break;
    case 22: foo = Foo{ std::in_place_index<22> }; break;
    case 23: foo = Foo{ std::in_place_index<23> }; break;
    case 24: foo = Foo{ std::in_place_index<24> }; break;
    case 25: foo = Foo{ std::in_place_index<25> }; break;
    default: foo = Foo{ std::in_place_index<26> }; break;
    }

    return std::visit(overloaded{
        [](std::monostate) { return  0; },
        [](Test<A> const&) { return  1; },
        [](Test<B> const&) { return  2; },
        [](Test<C> const&) { return  3; },
        [](Test<D> const&) { return  4; },
        [](Test<E> const&) { return  5; },
        [](Test<F> const&) { return  6; },
        [](Test<G> const&) { return  7; },
        [](Test<H> const&) { return  8; },
        [](Test<I> const&) { return  9; },
        [](Test<J> const&) { return 10; },
        [](Test<K> const&) { return 11; },
        [](Test<L> const&) { return 12; },
        [](Test<M> const&) { return 13; },
        [](Test<N> const&) { return 14; },
        [](Test<O> const&) { return 15; },
        [](Test<P> const&) { return 16; },
        [](Test<Q> const&) { return 17; },
        [](Test<R> const&) { return 18; },
        [](Test<S> const&) { return 19; },
        [](Test<T> const&) { return 20; },
        [](Test<U> const&) { return 21; },
        [](Test<V> const&) { return 22; },
        [](Test<W> const&) { return 23; },
//      [](Test<X> const&) { return 24; },  // Whoops...
        [](Test<Y> const&) { return 25; },
        [](Test<Z> const&) { return 26; },
        STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA
        }, foo
    );
}

-fmax-errors = 1(gcc)或-ferror-limit = 1(clang)时,std_visit_improve_compiler_errors_errors_errors_erors_lambda导致静态sattic Assertert,消息要打印出来,解释错误。但是,不幸的是,它并没有告诉我们哪种选择不满意,并且仍然不能阻止原始的,漫长的且实际上毫无智能的编译器错误被生成。但是,至少,错误的原因更为明显。

例如

$ g++ -std=c++17 -fmax-errors=1 -o example example.cc
...
example.cc:5:19: error: static assertion failed: non-exhaustive visitor
    5 |     static_assert(always_false_v<decltype(__args)...>, "non-exhaustive visitor"); \
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
example.cc:107:9: note: in expansion of macro ‘STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA’
  107 |         STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated due to -fmax-errors=1.

I've come up with a less-than-ideal solution, but it's better than nothing. If a better solution eventually comes along, I will happily switch the accepted answer to that.

Here's a proof of concept.

#include <variant>

#define STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA \
[](auto... __args) { \
    static_assert(always_false_v<decltype(__args)...>, "non-exhaustive visitor"); \
},

template <typename... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template <typename... Ts> overloaded(Ts...) -> overloaded<Ts...>;

template <typename> constexpr bool always_false_v = false;

template <typename> class Test { };

using Foo = std::variant<
    std::monostate,
    Test<struct A>,
    Test<struct B>,
    Test<struct C>,
    Test<struct D>,
    Test<struct E>,
    Test<struct F>,
    Test<struct G>,
    Test<struct H>,
    Test<struct I>,
    Test<struct J>,
    Test<struct K>,
    Test<struct L>,
    Test<struct M>,
    Test<struct N>,
    Test<struct O>,
    Test<struct P>,
    Test<struct Q>,
    Test<struct R>,
    Test<struct S>,
    Test<struct T>,
    Test<struct U>,
    Test<struct V>,
    Test<struct W>,
    Test<struct X>,
    Test<struct Y>,
    Test<struct Z>
    >;

int main(int argc, char const* argv[])
{
    Foo foo;

    switch (argc) {
    case  0: foo = Foo{ std::in_place_index< 0> }; break;
    case  1: foo = Foo{ std::in_place_index< 1> }; break;
    case  2: foo = Foo{ std::in_place_index< 2> }; break;
    case  3: foo = Foo{ std::in_place_index< 3> }; break;
    case  4: foo = Foo{ std::in_place_index< 4> }; break;
    case  5: foo = Foo{ std::in_place_index< 5> }; break;
    case  6: foo = Foo{ std::in_place_index< 6> }; break;
    case  7: foo = Foo{ std::in_place_index< 7> }; break;
    case  8: foo = Foo{ std::in_place_index< 8> }; break;
    case  9: foo = Foo{ std::in_place_index< 9> }; break;
    case 10: foo = Foo{ std::in_place_index<10> }; break;
    case 11: foo = Foo{ std::in_place_index<11> }; break;
    case 12: foo = Foo{ std::in_place_index<12> }; break;
    case 13: foo = Foo{ std::in_place_index<13> }; break;
    case 14: foo = Foo{ std::in_place_index<14> }; break;
    case 15: foo = Foo{ std::in_place_index<15> }; break;
    case 16: foo = Foo{ std::in_place_index<16> }; break;
    case 17: foo = Foo{ std::in_place_index<17> }; break;
    case 18: foo = Foo{ std::in_place_index<18> }; break;
    case 19: foo = Foo{ std::in_place_index<19> }; break;
    case 20: foo = Foo{ std::in_place_index<20> }; break;
    case 21: foo = Foo{ std::in_place_index<21> }; break;
    case 22: foo = Foo{ std::in_place_index<22> }; break;
    case 23: foo = Foo{ std::in_place_index<23> }; break;
    case 24: foo = Foo{ std::in_place_index<24> }; break;
    case 25: foo = Foo{ std::in_place_index<25> }; break;
    default: foo = Foo{ std::in_place_index<26> }; break;
    }

    return std::visit(overloaded{
        [](std::monostate) { return  0; },
        [](Test<A> const&) { return  1; },
        [](Test<B> const&) { return  2; },
        [](Test<C> const&) { return  3; },
        [](Test<D> const&) { return  4; },
        [](Test<E> const&) { return  5; },
        [](Test<F> const&) { return  6; },
        [](Test<G> const&) { return  7; },
        [](Test<H> const&) { return  8; },
        [](Test<I> const&) { return  9; },
        [](Test<J> const&) { return 10; },
        [](Test<K> const&) { return 11; },
        [](Test<L> const&) { return 12; },
        [](Test<M> const&) { return 13; },
        [](Test<N> const&) { return 14; },
        [](Test<O> const&) { return 15; },
        [](Test<P> const&) { return 16; },
        [](Test<Q> const&) { return 17; },
        [](Test<R> const&) { return 18; },
        [](Test<S> const&) { return 19; },
        [](Test<T> const&) { return 20; },
        [](Test<U> const&) { return 21; },
        [](Test<V> const&) { return 22; },
        [](Test<W> const&) { return 23; },
//      [](Test<X> const&) { return 24; },  // Whoops...
        [](Test<Y> const&) { return 25; },
        [](Test<Z> const&) { return 26; },
        STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA
        }, foo
    );
}

When compiling with -fmax-errors=1 (GCC) or -ferror-limit=1 (Clang), the STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA causes the static assert message to print out, explaining the error. Unfortunately, however, it does not tell us which alternative is unsatisfied and it still does not prevent the original, long and practically-unintelligible compiler error from being generated. At least, though, the cause of the error is more clear.

e.g.

$ g++ -std=c++17 -fmax-errors=1 -o example example.cc
...
example.cc:5:19: error: static assertion failed: non-exhaustive visitor
    5 |     static_assert(always_false_v<decltype(__args)...>, "non-exhaustive visitor"); \
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
example.cc:107:9: note: in expansion of macro ‘STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA’
  107 |         STD_VISIT_IMPROVE_COMPILER_ERRORS_LAMBDA
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated due to -fmax-errors=1.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文