6

我正在为 template 的重载解析中寻找位域的解决方法。

我有一个函数,我模板化了它的参数的完美转发:

template <typename... Args> void f(Args &&...args) { }

如果我尝试将它与位域参数一起使用,如下所示:

struct bits { unsigned int foo:1; };
bits b{1};
f(b.foo);

…它无法编译:

main.cpp:26:7:错误:非常量引用不能绑定到位域“foo”
    f(b.foo);
      ^~~~~

有没有一种方法可以重载f(),使其按值获取位字段,但在常见情况下仍通过引用获取其他参数?

到目前为止,我一直无法。例如,如果我添加一个按值接受参数的重载……</p>

main.cpp:27:5:错误:对“f”的调用不明确
    f(b.foo);
    ^
4

3 回答 3

2

http://coliru.stacked-crooked.com/view?id=b694c6cc3a52e0c14bedd6a26790d99d-e54ee7a04e4b807da0930236d4cc94dc

如果做得不好,也可以做到。我建议不要这样做。基本上,关键部分是因为您不能拥有对位域的指针或引用,所以您可以使用lambda来为您设置位域。

我和下一个人一样不喜欢宏,但这是我能想到的避免要求调用者在调用点输入 lambda 的唯一方法。

template<class assigner_type>
struct bitfieldref_type {
    bitfieldref_type(bool value, assigner_type&& assign) :value(value), assign(std::move(assign)) {}
    operator bool() const {return value;}
    bitfieldref_type& operator=(bool v) {assign(v); value=v; return *this;}
private:
    bool value;
    assigner_type assign;
};
template<class assigner_type>
bitfieldref_type<assigner_type> make_bitfieldref(bool value,  assigner_type&& assign)
{return {value, std::move(assign)};}
//macro is optional
#define bitfieldref(X) make_bitfieldref(X, [&](bool v)->void{X=v;})

用法:

template <class T, typename... Args> void proof_it_works(T&& first) 
{first = 0;}
template <class T, typename... Args> void proof_it_works(T&& first, Args &&...args) {
    first = 0;
    proof_it_works(std::forward<Args>(args)...);
}    
template <typename... Args> void f(Args &&...args) {proof_it_works(std::forward<Args>(args)...);}

int main() {
    struct bits { unsigned int foo:1; };
    bits b{1};
    int a = -1;
    float c = 3.14;
    f(a, bitfieldref(b.foo), c);
    std::cout << a << b.foo << c;
    return 0;
}

我只是注意到我的bitfieldref_type假设值是 a bool,而不是 a unsigned int,但我将把它作为用户的 excersize 来解决。

于 2013-06-04T21:21:00.887 回答
2

它无法完成(至少不是您尝试的方式),因为标准是这样说的(我的粗体强调):

13.3.3.1.4 引用绑定 [over.ics.ref]

4 然而,将引用绑定到特定参数的其他限制不基于引用和参数的类型,但不影响标准转换序列的形成。[示例:具有“对 int 的左值引用”参数的函数可能是可行的候选者,即使相应的参数是 int 位域。隐式转换序列的形成将 int 位域视为 int 左值并找到与参数的精确匹配。如果函数是通过重载决议选择的,由于禁止将非 const 左值引用绑定到位域(8.5.3),调用仍然是格式错误的。—结束示例]

这解释了为什么

  • 原始示例无法编译,因为引用无法绑定到位域
  • 添加重载template<typename... Arg> f(Args.. args)会给您带来歧义:重载结果以平局告终,并且引用绑定到位域的禁令从未发挥作用。
于 2013-06-04T20:49:11.303 回答
2

这是我能想到的最佳答案:

template <typename... Args> void f(Args &&...args) { }

struct bits { unsigned int foo:1; };

template <typename T> const T constipate(T v)
{ return(static_cast<const T>(v)); }

void bar()
{
bits b{1};
f(constipate(b.foo));
}

编辑:有一个更简单的解决方案,消除了对“便秘”模板的需要:

void bar()
{
bits b{1};
f(b.foo + 0);
}
于 2016-10-24T23:51:58.883 回答