因此,从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.rem
以i
初始化唯一的跨平台方式来执行此操作?
答案 0 :(得分:7)
你说明成员的顺序是不正确的。该定义继承自C,它明确指出它是(强调我的):
7.20.6.2 div,ldiv和lldiv函数
3 [...]结构应包含 (以任意顺序) 成员
quot
(商)和rem
(余数),每个都与参数numer
和denom
具有相同的类型。 [...]
在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 ", 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 ", const T &rem) { return {rem, qout}; }
int main() {
div_t d_t = make_div(1, 2);
}
如果您使用的是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();
}
如果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();
}
答案 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 ternary和without the ternary。
另一方面,clang3.9编译的代码with the ternary比without 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 gcc但fails 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函数中逐步初始化一个值。因此,我们可以正确地设置quot
和rem
,使其独立于它们在数据类型中出现的顺序。
通过将其包装到constexpr函数中,我们允许编译器完全优化对make_div
的调用: