{技术}{C++}常量字符串类型的模板函数特化问题

213 views
Skip to first unread message

Wenbo Yang

unread,
Aug 10, 2009, 8:36:11 AM8/10/09
to pon...@googlegroups.com
Hi All,

有一个模板函数特化的问题我想了很久,查了一些资料也不得其解,特地发贴来请教一下。简单的代码如下:

#include <iostream>
using namespace std;

template<typename T>
void foo(const T& t)
{
  cout << "foo: generic" << endl;
}

template<>
void foo<const char *>(const char * const& t)
{
  cout << "foo: special" << endl;
}

template<typename T>
void bar(const T t)
{
  cout << "bar: generic" << endl;
}

template<>
void bar<const char *>(const char * t)
{
  cout << "bar: special" << endl;
}

int main()
{
  foo("hello");                             // foo: generic
  foo(static_cast<const char *>("hello"));  // foo: special
  bar("hello");                             // bar: special
  return 0;
}


我的问题是在 main 函数中第一句的输出上,我不理解它为什么没有调用特化的函数。我用 g++ 4.2 和 VC8 的 cl 15 编译后的结果是一样的,都如注释中的输出,我想至少编译器在这方面可能是有个约定的。请大家不吝赐教,谢谢!

敬礼,
文博
--
Wenbo YANG

Homepage ---> http://solrex.cn
Blog | Solrex Shuffling ---> http://blog.solrex.cn

唐正华

unread,
Aug 10, 2009, 1:23:10 PM8/10/09
to pon...@googlegroups.com
参考资料:
模块特化的介绍:
http://www.chineselinuxuniversity.net/articles/26760.shtml
关于const的:

比较一下特化函数foo和bar,发现foo有一个const &,解释想了一个小时都没有想出来,等待高手解答。

另:想问一下,
 foo<const char *>("hello");
这种形式的调用和foo(static_cast<const char*>("hello")); 有什么区别?谢谢







2009/8/10 Wenbo Yang <sol...@gmail.com>



--
历史上的今天http://dzshare.cn

Ian Yang

unread,
Aug 10, 2009, 9:24:41 PM8/10/09
to pon...@googlegroups.com
类型不对. "abc"字符串的类型是char[]


只能用重载,因为不能偏特化:


简单形式:

void foo(const char* xxx);

这个prototype会和char[]匹配得更好


用数组指针:
template<size_t N>
void foo(const char (&t)[N])

不过用些编译器可以不是把"abc"作为数组,还需要同时考虑const char*, 不如上面那个。

函数的特化很不方便,更多时候通过重载来解决。

-------
Sincerely, Ian Yang


Sent from Shanghai, 31, China

2009/8/11 唐正华 <tangl...@gmail.com>

尚鹏飞

unread,
Aug 10, 2009, 10:35:35 AM8/10/09
to pon...@googlegroups.com
刚好前几天在学习C++ template.我就发表下自己的见解,如有不对之处还请指教:
将你的代码加入类型信息:
template<typename T>
void foo(const T& t)
{
  cout << "foo: generic" <<" "<<typeid(t).name()<<endl;
}
你可以看到其实的t是char const [6],而这正是字符串常量的类型,所以编译器将generic的实例化,而对于bar,因为generic中的参数是传值不是引用,C++规定对数组的传值要做decay所以special的更符合实例化要求。
以上内容可参考<<C++ Templates The Complete Guide>>中第五章第六节。
2009/8/10 Wenbo Yang <sol...@gmail.com>

学宁

unread,
Aug 10, 2009, 1:47:30 PM8/10/09
to pon...@googlegroups.com
字符串数组模版传引用的特例化是不太一样的,例如

#include <iostream>
#include <string>

template<class T>
inline T const & max (T const &a,T const &b)
{
return strcmp(a,b)? b:a;
}
int main()
{
std::string s;
::max("1","2");
::max("2","1");
::max("21","1");
::max("31",s);
}
会报编译错误,说const char[3]和const char[2]不是同个类型

下面是一个sample的写法,字符串比较
template <class T,int N,int M>
inline T const *max (T const (&a)[N],T const (&b)[M])
{
return strcmp(a,b)<0?a:b;
}

我修改了下你的代码,在vc8下编译得到的结果,应该比较能说明问题

#include <iostream>
using namespace std;

template<typename T>
void foo(const T& t)
{

cout << typeid(t).name() << endl;
}

template<>
void foo<const char *>(const char * const& t)
{

cout << typeid(t).name() << endl;
}

template<typename T>
void bar(const T t)
{

cout << typeid(t).name() << endl;
}

template<>
void bar<const char *>(const char * t)
{

cout << typeid(t).name() << endl;
}

int main()
{
foo("hello"); // char const [6]
foo(static_cast<const char *>("hello")); // char const *
bar("hello"); // char const *
return 0;
}
C++的template也刚学不久,仅抛砖

2009/8/11 唐正华 <tangl...@gmail.com>

xpol

unread,
Aug 13, 2009, 11:11:22 AM8/13/09
to TopLanguage
对于函数模板,这种情况通常用重载更直接一些:


//针对const char* const作为参数的重载
void foo(const char * const& t)
{
cout << "foo: special" << endl;

}

//针对const std::vector<T>作为参数的重载
template<typename T>
void foo(const std::vector<T>& t)
{
cout << "foo: vector<T> overload" <<endl;
}

然后使用:
foo(std::vector<char>());
foo("hello");

输出:

foo: vector<T> overload
foo: special

jinhu wang

unread,
Aug 17, 2009, 2:16:07 AM8/17/09
to pon...@googlegroups.com
我总觉得涉及到模版特化及偏特化的时候,c++的定义就显得不够严谨了,或者所牵扯的内容就会太复杂了。
就如下面这段代码,在g++下能正常编译运行了,但是在目标产品的编译器上根本编不过。。。
 
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class mystream
{
public:
    mystream():m_pos(0)
    {
        m_vContainer.reserve(1<<10);
    };
    virtual ~mystream(){};
    enum
    {
        BYTE_BIT_SIZE =8,
    };
    template <typename T > mystream& operator>>(T& a_iPara)
    {
        a_iPara = 0;
        for(int i=0; i<sizeof(T);++i)
        {
            a_iPara = ((a_iPara<<BYTE_BIT_SIZE)+m_vContainer[m_pos+i]);
        }
        m_pos += sizeof(T);
        return *this;
    }
    template <typename T > mystream&  operator<<(const T& a_iPara)
    {
        //unsigned char bttmp[sizeof(T)]=0;
        m_vContainer.insert(m_vContainer.end(),sizeof(T),0);
        vector<unsigned char >::iterator it= m_vContainer.end()-1;
        for(int i=0; i<sizeof(T);++i,--it)
        {
            *it = ((a_iPara>>(BYTE_BIT_SIZE*i))&0xff);
            //printf("operator[%d]=%x\n",i , *it);
        }
  return *this;
    }
    template <typename T > mystream& operator>>(T* a_iPara)
   {
        //cout<<"CHARSTAR"<<endl;
        if(NULL ==a_iPara)
        {
            return *this;
        }
        int i=0;
        int size = m_vContainer.size();
        int slen = 0;
        for(i=0; (i+m_pos)<size;++i )
        {
            if(m_vContainer[m_pos+i]=='\0')
            {
                slen = i;
                break;
            }
        }
        if((i+m_pos)==size)
        {
            return *this;
        }
        for(i=0; i<slen;++m_pos,++i )
        {
            a_iPara[i] = m_vContainer[m_pos];
            //cout<<m_vContainer[m_pos]<<endl;
        }
        a_iPara[i] = '\0';
        ++m_pos;
        return *this;
    }
    template <typename T >
    mystream& mystream::operator<< ( T* a_iPara)
    {
    if(NULL ==a_iPara)
  {
   m_vContainer.push_back(0);
   return *this;
  }
        for(int i=0;a_iPara[i]!='\0';++i )
        {
            m_vContainer.push_back((unsigned char)a_iPara[i]);
        }
cout<<"operator<< <T*>"<<endl;
        m_vContainer.push_back(0);
        return *this;
    }   
    template <typename T >
    mystream& mystream::operator<< (const T* a_iPara)
    {
    if(NULL ==a_iPara)
  {
   m_vContainer.push_back(0);
   return *this;
  }
        for(int i=0;a_iPara[i]!='\0';++i )
        {
            m_vContainer.push_back((unsigned char)a_iPara[i]);
        }
        m_vContainer.push_back(0);
        cout<<"operator<< <const T*>"<<endl;
        return *this;
    }
 protected:
    vector<unsigned char >m_vContainer;
    int m_pos;
};
template <>
mystream& mystream::operator>> <string>(string& a_iPara)
{
        a_iPara="";
        int i=0;
        int size = m_vContainer.size();
        int slen = 0;
        for(i=0; (i+m_pos)<size;++i )
        {
            if(m_vContainer[m_pos+i]=='\0')
            {
                slen = i;
                break;
            }
        }
        if((i+m_pos)==size)
        {
            return *this;
        }
        for(i=0; i<slen;++m_pos,++i )
        {
            a_iPara += m_vContainer[m_pos];
        }
        ++m_pos;
        cout<<"operator>> <string>"<<endl;
        return *this;
}
template <>
 mystream& mystream::operator<< <string>(const string& a_iPara)
 {
  cout<<"operator<< <string>"<<endl;
  return (*this)<<a_iPara.c_str();
 }
struct Test{};
mystream& operator<<(mystream&s,const Test&t)
{
   cout<<"Test&t>"<<endl;
 return s;
}
 
int main()
{
 printf("hello world!\n");
 char ptmp[]="hello!\n";
 char*p = "world\n";
 int i =1;
 int j=2;
 char ptmp1[20]="";
  char ptmp2[20]="";
 mystream s;
  s<<i<<ptmp<<p;
 s>>j>>ptmp1>>ptmp2;

 cout<<j<<endl;
 cout<<ptmp1<<endl;
 cout<<ptmp2<<endl;
 string str("this string\n");
 s<<str;
 string str2;
 s>>str2;
 cout<<str2;
 Test to;
 s<<to;
 return 0;
}


 
2009/8/13 xpol <xpo...@gmail.com>

liuxinyu

unread,
Aug 17, 2009, 6:15:55 AM8/17/09
to TopLanguage
贴一段Introduction to boost中的一段话,完全解答此问题:
>>>
That's all there is to it, but before you think that you've got all
this nailed down, answer this: What should be the type T in the
following declaration?

constant_type<T>::type hello(constant("Hello"));



Is it a char*? A const char*? No, it's actually a constant reference
to an array of six characters (the terminating null counts, too),
which gives us this:

constant_type<const char (&)[6]>::type
hello(constant("Hello"));



This isn't a pretty sight, and it's a pain for anyone who needs to
update the literalwhich is why I find it much cleaner to use the good
old std::string to get the job done.

constant_type<std::string>::type
hello_string(constant(std::string("Hello")));



This way, you have to type a little bit more the first time, but you
don't need to count the characters, and if there's ever a need to
change the string, it just works.


<<<
--
liuxinyu
http://sites.google.com/site/algoxy/
Reply all
Reply to author
Forward
0 new messages