【问题标题】:static_assert on class inheritance to terminate compilation (via SFINAE?)类继承上的 static_assert 以终止编译(通过 SFINAE?)
【发布时间】:2018-03-03 16:10:00
【问题描述】:

目标

假设template <class T> class MyThing 有大量using 语句,例如using T::my_defusing T::my_other_def。由于显而易见的原因,如果T 没有定义my_defmy_other_def,编译将失败。 然而,实际用例中的编译失败会导致数百个错误,使用户难以理解真正的问题。虽然编译失败“足够好”,但我真的很想通过某种聪明的代理类找到一种提前终止编译的方法。首先,为了完整起见,包含了 SFINAE 设置。其次是一个推论示例,说明如何使用 functions 进行此操作。然后是我的尝试,以及这可能实际上不可能的原因(但我希望是错误的)。

注意:这是一个“严格”的 C++11 项目(但我不反对导入类型,例如 std::void_t 已经成功,虽然我在 std::conditional 失败了)。如果您知道这些,请跳到第 2 部分并附注:

  • is_valid<T>() 是一个static constexpr bool 函数,其行为类似于std::conditional,检查my_defmy_other_defT 中的有效类型名。

1:SFINAE 设置(C++17 反向移植和好东西)

///////////////////////////////////////////////////////
// backport from C++17 for using in C++11
namespace internal { template <class...> using void_t = void; }
///////////////////////////////////////////////////////
// my_def checker
/* primary template handles types that have no nested ::my_def member: */
template <class, class = internal::void_t<>>
struct has_my_def : std::false_type { };
/* specialization recognizes types that do have a nested ::my_def member: */
template <class T>
struct has_my_def<T, internal::void_t<typename T::my_def>> : std::true_type { };
///////////////////////////////////////////////////////
// my_other_def checker
/* primary template handles types that have no nested ::my_other_def member: */
template <class, class = internal::void_t<>>
struct has_my_other_def : std::false_type { };
/* specialization recognizes types that do have a nested ::my_other_def member: */
template <class T>
struct has_my_other_def<T, internal::void_t<typename T::my_other_def>> : std::true_type { };
///////////////////////////////////////////////////////
// freaking sweet: https://stackoverflow.com/a/28253503/3814202
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

template <class T>
static constexpr bool is_valid() {
    return all_true< has_my_def<T>::value, has_my_other_def<T>::value >::value;
}

2。功能版本

这仅适用于调用函数。这种方法(据我所知)不能用于验证类,因为编译仍将继续。这里的“技巧”只是为函数调用添加足够的间接层。我不知道如何通过继承来做到这一点。

///////////////////////////////////////////////////////
// Function example
// does the real work
template <class T>
typename std::enable_if< is_valid<T>(), void>::type
do_actual_work() {
    // this never gets compiled from validate_wrapper<Almost_1>
    // and therefore never produces compiler warnings about
    // `my_other_def` not being a type
    using my_def = typename T::my_def;
    using my_other_def = typename T::my_other_def;

    my_def x = (my_def) 11;
    my_other_def y = (my_other_def) 12;

    std::cout << "A my_def: "       << x << std::endl
              << "A my_other_def: " << y << std::endl;
}

template <class T>
typename std::enable_if< !is_valid<T>(), void>::type
validate() {
    // Compilation ends here
    static_assert(is_valid<T>(), "You have been asserted.");
}

template <class T>
typename std::enable_if< is_valid<T>(), void>::type
validate() {
    std::cout << "Work gets done." << std::endl;
    do_actual_work<T>();
}

// user calls this function
template <class T>
void validate_wrapper() {
    validate<T>();
}

请参阅第 4 节了解演示此工作的类的定义。

3。尝试继承设置

static_assert 确实下降了,但“暴露”的TheClass(不是internal::TheClass)将继续尝试using 语句。对于这段代码的真正意图,这会产生数百个警告,这就是我尝试这样做的原因。

///////////////////////////////////////////////////////
// Need some more proxying?
namespace internal {
    template <class T, bool valid>
    struct TheClass {
        // compiler warning 1 (seek termination here)
        static_assert(valid, "You have been asserted.");
    };

    template <class T>
    struct TheClass<T, true> {
        std::string message() { return "You are valid."; }
    };
}

template <class T>
struct TheClass : public internal::TheClass<T, is_valid<T>()> {
    // compiler warning 2
    using my_def = typename T::my_def;
    // compiler warning 3
    using my_other_def = typename T::my_other_def;

    std::string message() {
        return internal::TheClass<T, is_valid<T>()>::message();
    }
};

4。一些简单的测试

///////////////////////////////////////////////////////
// The tests
struct Good {
    using my_def = float;
    using my_other_def = int;
};
struct Almost_1 { using my_def = double; };
struct Almost_2 { using my_other_def = bool; };
struct Bad {};

int main(int argc, const char **argv) {
    // sanity checks
    std::cout << std::boolalpha
              << "Good:     " << is_valid<Good>()     << std::endl
              << "Almost_1: " << is_valid<Almost_1>() << std::endl
              << "Almost_2: " << is_valid<Almost_2>() << std::endl
              << "Bad:      " << is_valid<Bad>()      << std::endl;

    // in function land, uncomment these and it will
    // only produce the single static_assert for valid<T>
    // rather than continuing on for breaking T
    // validate_wrapper<Good>();
    // validate_wrapper<Almost_1>();

    // At least this works.
    TheClass<Good> g;
    std::cout << "Good message: " << g.message() << std::endl;

    // I would like to achieve just one error from static_assert
    // comment out to see that `validate_wrapper<Almost_1>` will
    // terminate early (as desired)
    TheClass<Almost_1> a1;
    return 0;
}

所以最后,如果您在main不注释函数版本,您可以观察到我试图避免的相同行为。

  1. validate_wrapper&lt;Almost_1&gt;static_assert 被触发。
  2. 由于我们在同一个代码块中,TheClass&lt;Almost_1&gt; 仍将被编译并产生更多警告。
    • 如果您注释掉TheClass&lt;Almost_1&gt;,您将不会收到来自do_actual_work 的警告。
    • 类版本的类比是 main 当前正在编译,所以它会一直看到它(我理解的 SFINAE 的全部意义)。因此,由于TheClass&lt;Almost_1&gt; 已经开始编译,所以不管static_assert 在其基类中是否继续,它都会一直编译到最后。

有什么巧妙的延迟继承的方法吗?我尝试了curiously recurring template pattern 的一种变体,认为这将是如何做到这一点,但实际上它最终只是产生了更多警告。

【问题讨论】:

  • 我可能处于愚蠢的模式,但我找不到你在哪里陈述了你真正想做的事情......
  • 不,是我!我想尽办法让问题尽可能简短,这样做有效地消除了实际问题。通过将其与功能版本的工作方式进行比较,最后的问题引导和概要现在应该使事情更清楚了。

标签: c++11 sfinae static-assert


【解决方案1】:

希望有另一种方法,但似乎最简单的解决方案是另一种方法:用户应该实例化的类没有实际实现,而 internal 类可以完成所有工作。

namespace internal {
    template <class T, bool valid>
    struct TheClass {
        static_assert(valid, "Template 'class T' must have static types `my_def` and `my_other_def`.");
    };

    template <class T>
    struct TheClass<T, true> {
        // compiler warning 2
        using my_def = typename T::my_def;
        // compiler warning 3
        using my_other_def = typename T::my_other_def;

        std::string message() {
            return "work gets done";
        }
    };
}

template <class T>
struct TheClass : internal::TheClass<T, is_valid<T>()> { };

struct Good {
    using my_def = float;
    using my_other_def = int;
};
struct Almost_1 { using my_def = double; };

int main(int argc, const char **argv) {
    TheClass<Good> g;
    std::cout << "Good message: " << g.message() << std::endl;

    // only one static assert
    TheClass<Almost_1> a1;
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    • 2021-04-24
    • 2023-03-07
    • 1970-01-01
    • 2020-01-25
    • 2012-02-04
    • 2021-03-18
    相关资源
    最近更新 更多