【问题标题】:How can I Initialize a div_t Object?如何初始化 div_t 对象?
【发布时间】:2017-01-09 20:20:40
【问题描述】:

所以从div 函数返回的成员的顺序似乎是实现定义的。

quot 是第一个st 成员还是rem

假设我正在做这样的事情:

generate(begin(digits), end(digits), [i = div_t{ quot, 0 }]() mutable {
    i = div(i.quot, 10);
    return i.rem;
})

当然,这里的问题是我不知道我是否在我的 lambda 捕获中初始化了 i.quoti.rem。用div(quot, 1) 初始化i 是唯一的跨平台方法吗?

【问题讨论】:

  • @W.F.如果您愿意取消删除它,您的constexpr 答案似乎就是我想要的...

标签: c++ division member modulo


【解决方案1】:

您说得对,成员的顺序未指定。该定义继承自 C,它明确声明它是(强调我的):

7.20.6.2 div、ldiv 和 lldiv 函数

3 [...] 结构应包含 (按任意顺序) 成员 quot(商)和 rem(余数),每个都与参数numerdenom 具有相同的类型。 [...]

在 C 中,未指定顺序这一事实并不重要,并且包含一个专门针对 div_t 的示例:

6.7.8 初始化

34 示例 10 可以将结构成员初始化为非零值,而不依赖于它们的顺序:

div_t answer = { .quot = 2, .rem = -1 };

不幸的是,C++ 从未采用这种语法。

我可能会在辅助函数中进行简单的赋值:

div_t make_div_t(int quot, int rem) {
  div_t result;
  result.quot = quot;
  result.rem = rem;
  return result;
}

对于普通的int 值,无论您使用初始化还是赋值都无关紧要,它们具有相同的效果。

您除以1 也是一个有效选项。

【讨论】:

    【解决方案2】:

    引用 C11 标准草案 N1570 §7.22.6.2

    div、ldiv 和 lldiv 函数分别返回一个 div_t、ldiv_t 和 lldiv_t 类型的结构,包括商和余数。结构应包含(以任意顺序)成员 quot(商)和 rem(余数),每个成员都与参数 numer 和 denom 具有相同的类型。

    所以在这种情况下,div_t 是一个普通的 POD 结构,由两个 ints 组成。

    所以你可以像每个普通结构一样初始化它,你的方式也是我会做的。它也是便携式的。

    否则我找不到任何特殊的机制来初始化它们,无论是在 C 中还是在 C++ 标准中。但是对于 POD 又名普通旧数据类型,没有任何必要。

    【讨论】:

    • 问题的全部意义在于quotrem 可能是第一个成员,而您提供的报价(我也这样做了)明确表明OP 的担忧是有道理。这根本不支持您的结论,即使用 {quot, rem} 对其进行初始化就可以了。它是有效的,但它不一定会做 OP 想要的。
    • @hvd 很遗憾你是对的,我只是在考虑零初始化
    【解决方案3】:

    编辑:

    我认为 VS 的解决方法可能如下所示:

    #include <cstdlib>
    #include <type_traits>
    
    template<class T>
    struct DTMaker {
     using D = decltype(div(T{}, T{}));
     static constexpr D dt = D{0,1};
     static constexpr auto quot = dt.quot;
    };
    
    template <class T, typename std::enable_if<DTMaker<T>::quot == 0>::type* = nullptr>
    typename DTMaker<T>::D make_div(const T &quot, const T& rem) { return {quot, rem}; }
    
    template <class T, typename std::enable_if<DTMaker<T>::quot == 1>::type* = nullptr>
    typename DTMaker<T>::D make_div(const T &quot, const T &rem) { return {rem, qout}; }
    
    int main() {
       div_t d_t = make_div(1, 2);
    }
    

    [live demo]

    旧答案:

    如果您使用的是 c++17,您还可以尝试使用结构化绑定、constexpr 函数和 SFINAE 重载来检测结构中首先声明的字段:

    #include <cstdlib>
    #include <algorithm>
    #include <iterator>
    
    constexpr bool first_quot() {
        auto [x, y] = std::div_t{1, 0};
        (void)y;
        return x;
    }
    
    template <bool B = first_quot()>
    std::enable_if_t<B, std::div_t> foo() {
        int quot = 1;
        int rem = 0;
        return {quot, rem};
    }
    
    template <bool B = first_quot()>
    std::enable_if_t<!B, std::div_t> foo() {
        int quot = 1;
        int rem = 0;
        return {rem, quot};
    }
    
    int main() {
        foo();
    }
    

    [live demo]

    或者更简单的使用 if constexpr:

    #include <cstdlib>
    #include <algorithm>
    #include <iterator>
    
    constexpr bool first_quot() {
        auto [x, y] = std::div_t{1, 0};
        (void)y;
        return x;
    }
    
    std::div_t foo() {
        int quot = 1;
        int rem = 0;
        if constexpr(first_quot())
            return {quot, rem};
        else
            return {rem, quot};
    }
    
    int main() {
        foo();
    }
    

    [live demo]

    【讨论】:

      【解决方案4】:

      试试这样的:)

      int quot = 10;
      auto l = [i = [=] { div_t tmp{}; tmp.quot = quot; return tmp; }()]() mutable
      {
          i = div(i.quot, 10);
          return i.rem;
      };
      

      看起来像在 C 中使用复合文字。:)

      或者您可以通过在 lambda 表达式之外定义变量 i 并通过引用在 lambda 中使用它来简化任务。

      例如

      int quot = 10;
      dov_t i = {};
      i.quot = quot;
      
      auto l = [&i]()
      {
          i = div(i.quot, 10);
          return i.rem;
      };
      

      【讨论】:

      • 啊!这是 lambda 感知。不确定这个笑话在这里是否有效,但我试过了。无论如何,答案似乎表明成员初始化是我前进的唯一途径:(
      • @JonathanMee 求值的 lambda 表达式是一个表达式。所以没有问题。它必须工作。表达式的值是一个 div_t 类型的对象。
      【解决方案5】:

      你可以使用三元来初始化它:

      generate(rbegin(digits), rend(digits), [i = div_t{ 1, 0 }.quot ? div_t{ quot, 0 } : div_t{ 0, quot }]() mutable {
          i = div(i.quot, 10);
          return i.rem;
      });
      

      例如,gcc6.3 将编译相同的代码 with the ternarywithout the ternary

      另一方面,clang3.9 编译的代码 with the ternarywithout the ternary 编译的代码要长。

      因此,三元是否被优化会因编译器而异。但在所有情况下,它都会为您提供不需要编写辅助函数的实现独立代码。


      顺便说一句,如果您要创建一个辅助函数来创建 div_t(或任何其他 div 返回),您可以这样做:

      template <typename T>
      enable_if_t<decltype(div(declval<T>(), declval<T>())){ 1, 0 }.quot != 0, decltype(div(declval<T>(), declval<T>()))> make_div(const T quot, const T rem) { return { quot, rem }; }
      template <typename T>
      enable_if_t<decltype(div(declval<T>(), declval<T>())){ 1, 0 }.quot == 0, decltype(div(declval<T>(), declval<T>()))> make_div(const T quot, const T rem) { return { rem, quot }; }
      

      请注意此does work on gccfails to compile on Visual Studio 因为某些不符合项。

      【讨论】:

      • 我暂时没有创建解决方法,但没有放弃,我稍后会尝试做
      • @W.F.这里有一些非常惊人的答案:stackoverflow.com/q/41593649/2642059 我个人最喜欢的是Simon Kramer's,因为它没有添加可能被无意劫持的默认模板参数。
      • @JonathanMee 很高兴你喜欢我的回答。但是,您不必担心有人劫持了 Yakk 答案中不可能的其他模板参数。特别是因为它们没有被使用,而你也使用std::enable_ifcounter_types,即使指定了,在声明时也不会改变任何内容。
      • @SimonKraemer 有趣。我需要阅读所有这些东西才能理解它。谢谢。
      • @JonathanMee 也许像this 这样的东西会起作用?
      【解决方案6】:

      我的解决方案使用一个 constexpr 函数,该函数本身包装并执行一个 lambda 函数,该函数根据模板参数确定并初始化正确的 div_t

      template <typename T>
      constexpr auto make_div(const T quot, const T rem)
      {
          return [&]() {
              decltype(std::div(quot, rem)) result;
              result.quot = quot;
              result.rem = rem;
              return result;
          }();
      }
      

      这适用于 MSVC15、gcc 6.3 和 clang 3.9.1。

      http://rextester.com/AOBCH32388


      lambda 允许我们在 constexpr 函数中逐步初始化一个值。所以我们可以正确地设置quotrem,并且独立于它们在数据类型本身中出现的顺序。

      通过将其包装到 constexpr 函数中,我们允许编译器完全优化对 make_div 的调用:

      叮当声:https://godbolt.org/g/YdZGkX

      gcc:https://godbolt.org/g/sA61LK

      【讨论】:

      • 我假设包装好的 lambda 允许我们在 Visual Studio 中编译?否则你可能会这样做:decltype(div(declval&lt;T&gt;(), declval&lt;T&gt;())) make_div(const T quot, const T rem)
      • @JonathanMee 我不明白你想告诉我什么。 make_div 使用 auto 是因为我希望类型依赖于 lambda 的返回值,而 lambda 使用 auto (不可见)因为我不希望返回类型到处都是。多次致电decltype 毫无意义。 lambda 之所以存在,是因为您不能直接在 constexpr 函数中创建未初始化的变量。此解决方案采用完全不同的方式,没有 SFINAE 问题。
      • 我想要传达的是 lambda 应该是不必要的:ideone.com/0yTqWP 我假设您正在使用它在 Visual Studio 2015 中编译?
      • 嗯,这对我来说是错误的,因为 “函数体必须被删除或默认或包含任何语句,除了 - 非文字类型变量的定义”我> (en.cppreference.com/w/cpp/language/constexpr)。也许您应该提出另一个问题,根据标准询问哪个版本是正确的。
      • 我在这里提出了一个新问题:stackoverflow.com/q/41618576/2642059 但我认为literal types 非常包容。所以我希望 lambda 不应该是必需的,而 Visual Studio 又错了。
      猜你喜欢
      • 1970-01-01
      • 2013-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-17
      • 2020-04-21
      相关资源
      最近更新 更多