如何初始化div_t对象?

时间:2017-01-09 20:20:41

标签: c++ division member modulo

因此,从div函数返回的成员的顺序似乎是实现定义的。

{1}是1 st 成员还是quot

让我们说我正在做这样的事情:

rem

当然这里的问题是我不知道我是否在lambda捕获中初始化generate(begin(digits), end(digits), [i = div_t{ quot, 0 }]() mutable { i = div(i.quot, 10); return i.rem; }) i.quot。是否i.remi初始化唯一的跨平台方式来执行此操作?

6 个答案:

答案 0 :(得分:7)

你说明成员的顺序是不正确的。该定义继承自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的除法也是一个有效选项。

答案 1 :(得分:1)

引用C11标准草案N1570§7.22.6.2

  

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

因此,在这种情况下,char是一个简单的 POD 结构,由两个div_t组成。

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

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

答案 2 :(得分: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;
};

答案 3 :(得分:1)

编辑:

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

OLD ANSWER:

如果您使用的是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]

如果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 :(得分:1)

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

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,因为有些不符合。

答案 5 :(得分:1)

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

clang:https://godbolt.org/g/YdZGkX

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