【问题标题】:Passing reference type as template argument in nested template structure does not work在嵌套模板结构中将引用类型作为模板参数传递不起作用
【发布时间】:2017-11-04 04:41:51
【问题描述】:

我很困惑,我正在设计一个模板,我发现使用T=float& 实例化模板时出现奇怪的行为:

// Given an available float f:
float f = 1.0;

// This getter works for T=float&:
template <typename T>
struct test {
  const T get() { return f; }
};

int main() {
  float& f1 = test<float&>().get();
}

第一个奇怪的事情是 f1 应该是 const float&amp; 代码是正确的,因此,我预计会出现错误,但它工作正常。

第二个奇怪的事情是在这个类似的例子中,当我不希望它报告错误时:

// Given an available float f:
float f = 1.0;

struct State {
  const float& get() { return f; }
};

// This does not work for T=float&:
template <typename T>
struct test2 {
  State state;
  const T get() { return state.get(); }
};

int main() {
  const float& f2 = test2<float&>().get();
}

报错是这样的:

main.cpp: In instantiation of 'const T test2<T>::get() [with T = float&]':
main.cpp:31:41:   required from here
main.cpp:22:36: error: binding reference of type 'float&' to 'const float' discards qualifiers
   const T get() { return state.get(); }

这很奇怪,因为第二个例子只声明了const float&amp;类型,而不是float&amp;而不是const float,所以我不知道发生了什么。

也许模板不是为使用引用而设计的,或者它是 GCC 上的一个错误,或者我只是在做一些愚蠢的事情。

我使用gcc (GCC) 6.3.1 20170306repl.it 网站上使用C++11 测试了这段代码。

另外,如果它是一个错误,我会对任何可用的解决方法感兴趣。

【问题讨论】:

  • 实际上我不希望得到const float&amp;,因为那是对 const float 的引用。当您将 T=float&amp; 替换为 const T 时,您会得到 const (float&amp;),但我认为您实际上不能形成 const 引用(不同于对 const 的引用)
  • 嗯,有道理。所以不可能通过使用T=float&amp; 来使函数返回const float&amp;?有没有办法模拟这种行为?对于我的模板的用户来说,让它有时返回float 有时返回float&amp; 是有意义的。另一种方法是创建一个具有不同名称的第二个模板来涵盖这两种情况。
  • const typename std::remove_reference&lt;T&gt;::type&amp;,也许
  • 嗯,这是一个不错的技巧,但用户将无法通过传递 T=float 使函数仅返回 const float。毕竟我可能最终会为这个案例编写第二个模板。

标签: c++ c++11 templates gcc pass-by-reference


【解决方案1】:

请看this问题和this问题。

同样的事情也发生在你的案例中。

对于第一种情况,我们的类型是float&amp;。而在float &amp; const 中,const 是多余的。所以它只会被解析为float &amp;

对于第二种情况,State::get() 返回的是对 const 浮点值的引用。而test2&lt;float&amp;&gt;::get() 返回的仍然是float&amp;。现在编译器将阻止您将 const float 分配给非常量。

【讨论】:

  • 你的意思是“const float value”而不是“const integer value”,但我明白了,似乎是正确的。您是否知道根据模板参数类型在模板中创建函数以返回 const float&amp;const float 的任何习惯用法?我正在考虑或从模板中删除const 字样,并在用户想要指定类型时强制使用const float&amp;const float 类型。
  • 是的,我编辑了答案。对于成语,我认为@Even young 给出了你想要的答案。
【解决方案2】:

只是更专业。我认为这给出了你想要的行为:

template <typename T>
struct test {
    T get() { return f; }
};

template <typename T>
struct test<T&> {
    const T& get() { return f; }
};

测试:

int main() {      
    const float& f1 = test<float&>().get();
    float& f2 = test<float&>().get(); //Error
    const float& f3 = test<const float&>().get();
    float f4 = std::move(test<float>().get());
}

【讨论】:

  • 很好,我花了一些时间来理解专业化,但它完全符合我的要求。只是为了确认我的解释是否正确:只有当参数是引用时才会使用特化,否则它将使用非特化版本对吗?
  • @VinGarcia 正确。
【解决方案3】:

为了完整起见,这是我可能会采用的解决方案:

template <class T>
struct make_non_modifiable { using type = const T; };
template <class T>
struct make_non_modifiable<T&> { using type = const T &; };
template <class T>
struct make_non_modifiable<T*> { using type = const T *; };

template <typename T>
struct test {
  typename make_non_modifiable<T>::type get() { return f; }
};

我没有尝试编译这段代码,所以可能有一两个错字。
我通常更喜欢类模板部分特化而不是函数模板特化,因为后者与函数重载一起更难理解。

【讨论】:

  • 非常有趣的方法,这可能会使我的代码更短,我会测试一下。
猜你喜欢
  • 2017-07-03
  • 1970-01-01
  • 2019-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多