将除字符串以外的所有类型按值传递给模板函数
Passing by value all types but string to a template function
我想定义一个模板函数,它为除 std::string
(和 const char*
)之外的所有类型获取一个按值传递的参数。
template<typename T>
void foo( T value )
{
// some code using value
}
std::string
版本的行为应与 template
版本完全相同,但其参数由 const&
传递。
在不复制 foo()
正文的情况下完成我想做的事情的最佳方法是什么?
我能想到的最好的方法是使用 value
将 代码包装在另一个函数中,然后在 foo()
的所有版本中调用它(template
版本和 std::string
重载)。还有别的办法吗?例如,是否可以从 std::string
重载中调用 template
版本?
编辑
我想知道的是避免各种特化和重载之间的代码重复的良好经验法则。什么是可以遵循的好模式?我应该为主体定义一个包装函数,然后从所有 overloads/specializations 中调用它,还是有其他方法?
简单的解决方案:为 std::string
:
提供重载
void foo( std::string const &value ) {
// some code using value
}
使用 std::enable_if
+ std::is_convertibale
:
template<typename T>
typename std::enable_if<!std::is_convertible<T,std::string>::value>::type foo( T value )
{
// some code using value
}
您可以定义类似类型特征的 class,它将 std::string
转换为 std::string&
并保留所有其他类型的类型:
template<class T>
struct helper {
typedef T type;
};
template<>
struct helper<std::string> {
typedef std::string& type; // or const std::string& type if you want
};
template<typename T>
void foo( typename helper<T>::type value, T value2 )
{
value = value2;
}
int main()
{
int a = 10;
foo(a, 42);
std::cout << a << std::endl; // prints 10
std::string s = "abc";
foo(s, std::string("def"));
std::cout << s << std::endl; // prints def
}
完整示例:http://coliru.stacked-crooked.com/a/96cf78e6c4846172
UPD:如@PiotrSkotnicki 所述,只有一个参数会使类型推导失败。但是,我会保留答案,因为如果您确实有多个 T
类型的参数,或者如果您可以为 foo
.
指定显式模板参数,它可能会有所帮助
UPD2:要解决类型推导问题,您可以添加另一个包装器:
template<typename T>
void foo_helper( typename helper<T>::type value )
{
value = T();
}
template<typename T>
void foo(T& value)
{
foo_helper<T>(value);
}
这仍然可能存在一些问题,所以这是否适用于您的用例,由您决定。
我认为您正在寻找的是 C++ 11 中的右值签名。
它很简单:
#include <iostream>
#include <string>
template<typename T>
void foo(T&& value)
{
std::cout << "was passed by refernece:" << std::is_lvalue_reference<T&&>::value << std::endl;
std::cout << value << std::endl;
}
int main()
{
std::string text = "hello";
foo(text);
foo(1);
}
您可以通过引用或值传递参数,右值规则将使用适当的类型。
为了避免代码重复, 可以扩展为从重载中实际调用模板:
#include <string>
#include <iostream>
#include <type_traits>
#include <boost/core/demangle.hpp>
template<typename T>
void foo( T value )
{
std::cout << "inside template" << std::endl;
std::cout << boost::core::demangle(typeid(value).name()) << std::endl;
}
void foo(const std::string &value)
{
std::cout << "inside const string overload" << std::endl;
foo<const std::string&>(value);
}
int main()
{
foo(10);
foo(std::string("hello"));
return 0;
}
输出
inside template
int
inside const string overload
inside template
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
我想定义一个模板函数,它为除 std::string
(和 const char*
)之外的所有类型获取一个按值传递的参数。
template<typename T>
void foo( T value )
{
// some code using value
}
std::string
版本的行为应与 template
版本完全相同,但其参数由 const&
传递。
在不复制 foo()
正文的情况下完成我想做的事情的最佳方法是什么?
我能想到的最好的方法是使用 value
将 代码包装在另一个函数中,然后在 foo()
的所有版本中调用它(template
版本和 std::string
重载)。还有别的办法吗?例如,是否可以从 std::string
重载中调用 template
版本?
编辑
我想知道的是避免各种特化和重载之间的代码重复的良好经验法则。什么是可以遵循的好模式?我应该为主体定义一个包装函数,然后从所有 overloads/specializations 中调用它,还是有其他方法?
简单的解决方案:为 std::string
:
void foo( std::string const &value ) {
// some code using value
}
使用 std::enable_if
+ std::is_convertibale
:
template<typename T>
typename std::enable_if<!std::is_convertible<T,std::string>::value>::type foo( T value )
{
// some code using value
}
您可以定义类似类型特征的 class,它将 std::string
转换为 std::string&
并保留所有其他类型的类型:
template<class T>
struct helper {
typedef T type;
};
template<>
struct helper<std::string> {
typedef std::string& type; // or const std::string& type if you want
};
template<typename T>
void foo( typename helper<T>::type value, T value2 )
{
value = value2;
}
int main()
{
int a = 10;
foo(a, 42);
std::cout << a << std::endl; // prints 10
std::string s = "abc";
foo(s, std::string("def"));
std::cout << s << std::endl; // prints def
}
完整示例:http://coliru.stacked-crooked.com/a/96cf78e6c4846172
UPD:如@PiotrSkotnicki 所述,只有一个参数会使类型推导失败。但是,我会保留答案,因为如果您确实有多个 T
类型的参数,或者如果您可以为 foo
.
UPD2:要解决类型推导问题,您可以添加另一个包装器:
template<typename T>
void foo_helper( typename helper<T>::type value )
{
value = T();
}
template<typename T>
void foo(T& value)
{
foo_helper<T>(value);
}
这仍然可能存在一些问题,所以这是否适用于您的用例,由您决定。
我认为您正在寻找的是 C++ 11 中的右值签名。
它很简单:
#include <iostream>
#include <string>
template<typename T>
void foo(T&& value)
{
std::cout << "was passed by refernece:" << std::is_lvalue_reference<T&&>::value << std::endl;
std::cout << value << std::endl;
}
int main()
{
std::string text = "hello";
foo(text);
foo(1);
}
您可以通过引用或值传递参数,右值规则将使用适当的类型。
为了避免代码重复,
#include <string>
#include <iostream>
#include <type_traits>
#include <boost/core/demangle.hpp>
template<typename T>
void foo( T value )
{
std::cout << "inside template" << std::endl;
std::cout << boost::core::demangle(typeid(value).name()) << std::endl;
}
void foo(const std::string &value)
{
std::cout << "inside const string overload" << std::endl;
foo<const std::string&>(value);
}
int main()
{
foo(10);
foo(std::string("hello"));
return 0;
}
输出
inside template
int
inside const string overload
inside template
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >