如何初始化 div_t 对象?

How can I Initialize a div_t Object?

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

quot 是第 1st 成员还是 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 是唯一的跨平台方式吗?

引用C11标准草案N1570 §7.22.6.2

The div, ldiv, and lldiv functions return a structure of type div_t, ldiv_t, and lldiv_t, respectively, comprising both the quotient and the remainder. The structures shall contain (in either order) the members quot (the quotient) and rem (the remainder), each of which has the same type as the arguments numer and denom.

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

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

否则我找不到任何特殊的机制来初始化它们,无论是在 C 还是 C++ 标准中。但是对于 POD aka Plain Old Datatypes,没有任何必要。

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

7.20.6.2 The div, ldiv, and lldiv functions

3 [...] The structures shall contain (in either order) the members quot (the quotient) and rem (the remainder), each of which has the same type as the arguments numer and denom. [...]

在 C 中,未指定顺序这一事实无关紧要,并且包含一个专门针对 div_t:

的示例

6.7.8 Initialization

34 EXAMPLE 10 Structure members can be initialized to nonzero values without depending on their order:

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 也是一个有效选项。

尝试这样的事情:)

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;
};

编辑:

我认为 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]

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

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 ternary and without the ternary.

另一方面,clang3.9 编译更长的代码with the ternary than it does without the ternary

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


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

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 gcc but 因为有些不一致。

我的解决方案使用一个 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