模板类型扣除参考

时间:2010-09-24 11:48:34

标签: c++ templates c++11

我一直在玩类型扣除/打印使用模板的代码形式:

#include <iostream>
template <typename T>
class printType {};

template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T>&)
{
    os << "SomeType"; return os;
}  

template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T*>&)
{
    os << printType<T>() << "*"; return os;
}  

template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T&>&)
{
    os << printType<T>() << "&"; return os;
}  
// etc... and can call on a variable through

template <typename T>
printType<T> print(T) { return printType<T>(); }  

int main()
{
    int a = 7;
    int *p = &a;
    int &r = a;

    //OK: return SomeType*
    std::cout << "type of p: " << print(p) << std::endl;
    //Hmmmm: returns SomeType <- (no &: can I get around this?)
    std::cout << "type of r: " << print(r) << std::endl;
}

我想知道我是否可以获得最后一行返回int&,即: (i)让函数模板print将其参数的类型推导为int&,或者以某种方式解决它应该在我传递它时返回printType<T&>;或
(ii)由于变量传递给函数的方式,这是否是不可避免的。

通过改变印刷形式或使用其他模板技巧,有没有办法解决这个问题?如果解决方案存在,我更喜欢非C ++ 0x,但总是很高兴看到将来可以提供哪些捷径(如果还没有)。

3 个答案:

答案 0 :(得分:10)

没有办法解决这个问题。表达式p,其中p命名引用,始终具有引用引用的类型。没有表达式具有类型T&。因此,您无法检测表达式是否源自引用。

使用C ++ 0x也无法做到这一点。 C ++的一个深层原则是没有具有引用类型的表达式。您可以编写decltype(r)以获取r个名称的类型,而不是表达式r具有的类型。但你不能写print(r),除非print当然是一个宏,但我不明白为什么你会走那条可怕的道路。

答案 1 :(得分:1)

我接受了我之前所说的话。我想我可能有一种方法可以用纯c / c ++来完成这项工作,尽管它非常混乱。你会 需要将指针传递给你的函数......

即。 bool hello_world(std :: string&amp; my_string,const std :: string * const my_string_ptr){

bool hello_world(std :: string my_string,const std :: string * const my_string_ptr){

如果你现在测试了

if(&amp; my_string == my_string_ptr)

如果var通过引用传递,它将评估 true ,如果通过传递,则评估为false。

当然,在所有功能中加倍变量可能是不值得的......


约翰内斯是对的...不是纯粹的c ++。但你可以做到这一点。诀窍是作弊。使用像perl这样的嵌入式脚本语言来搜索你的源代码。这是一个嵌入式perl模块:

http://perldoc.perl.org/perlembed.html

将函数名称,变量名称和源位置传递给它,然后使用正则表达式查找变量并检查其类型。实际上,对于您的代码来说,这可能是一个更好的解决方案,假设您总是会有一个方便的源代码。

我会稍微发布这个基本方法的功能......要照顾一些上午的工作! :)

即使您不想分发源代码,也可以创建某种打包函数/ var数据文件,您可以通过@运行时解析它并获得相同的结果。


修改1

例如......使用Perl Embed教程中的# I32 match(SV *string, char *pattern)函数,您可以执行以下操作:

bool is_reference(const char * source_loc, const char * function_name, 
                  const char * variable_name) {
   std::ifstream my_reader;
   char my_string[256];
   SV * perl_line_contents;
   bool ret_val = false;
   char my_pattern [400]=strcat("m/.*",function_name);
   my_pattern=strcat(my_pattern, ".*[,\s\t]*");
   my_pattern=strcat(my_pattern, variable_name);
   my_pattern=strcat(my_pattern, "[\s\t]*[\(,].*$");

   my_reader.open(source_loc.c_str());
   while (!my_reader.eof()) {
      my_reader.getline(my_string,256);
      sv_setpv(perl_line_contents,my_string);
      if(match(perl_line_contents,my_pattern)) {
          ret_val= true;
      }
   }

   return ret_val;
}

......有......两种方法(见上文更新)。

答案 2 :(得分:1)

您可以将SFINAE用于整数类型或任何可以通过“0”转换的方式:

template <typename T>
class is_reference
{
    struct yes { char a, b; };
    typedef char no;

    template <typename U> static no test(T y) { }
    template <typename U> static yes test(...) { }

public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

#include <iostream>

struct not_constructable_from_int {};
struct constructable_from_int { constructable_from_int(int x) { } };

int
main()
{
    std::cout << is_reference<int>::value << std::endl;
    std::cout << is_reference<int&>::value << std::endl;

    std::cout << is_reference<not_constructable_from_int>::value << std::endl;
    std::cout << is_reference<not_constructable_from_int&>::value << std::endl;
    std::cout << is_reference<constructable_from_int>::value << std::endl;
    std::cout << is_reference<constructable_from_int&>::value << std::endl;
}

请注意,测试基本上是“您可以致电test<T>(0)”,如果T是参考,如果T只是{您无法从0转换的课程。不幸的是,您可以随时致电test<T>(T()),这让我感到惊讶(即使Tint)。

正如您所看到的那样,您是否愿意从int开始构建类型,然后测试会对它们起作用,考虑到test<T>(T())结果,这真的让我感到困惑......