C++ 是否可以使用模板元编程有条件地禁用全局函数定义?

C++ 是否可以使用模板元编程有条件地禁用全局函数定义?,c++,templates,template-meta-programming,conditional-compilation,C++,Templates,Template Meta Programming,Conditional Compilation,假设我在单个参数上有一个简单的空模板函数,有两个专门化,一个用于无符号long,另一个用于size\u t(内容不重要): 根据我的想法,这个问题可以通过简单地禁用size\t函数定义来解决,因为任何试图调用f()的代码都会自动解析为f()。但是,对于定义size\u t不同于无符号long的平台,应该启用该功能 我读过一些关于模板元编程和SFINAE的书,我一直在玩这样的游戏: std::enable_if<(sizeof(size_t) > sizeof(unsigned lon

假设我在单个参数上有一个简单的空模板函数,有两个专门化,一个用于
无符号long
,另一个用于
size\u t
(内容不重要):

根据我的想法,这个问题可以通过简单地禁用
size\t
函数定义来解决,因为任何试图调用
f()
的代码都会自动解析为
f()
。但是,对于定义
size\u t
不同于
无符号long
的平台,应该启用该功能

我读过一些关于模板元编程和SFINAE的书,我一直在玩这样的游戏:

std::enable_if<(sizeof(size_t) > sizeof(unsigned long))>::type 
std::enable_if sizeof(unsigned long))>::type
但如果可能的话,我不确定如何使用这样的片段来禁用全局函数定义


那么,有没有办法使用模板元编程有条件地禁用全局函数定义?或者,更一般地说,我是走对了路,还是走错了路?

这在任何情况下都是可行的,但它有点乏味,对于更多的专业化来说,它的扩展性不好:

template<typename T
       , std::enable_if_t<!std::is_same<T, unsigned long>::value
                       && !std::is_same<T, size_t>::value>* = nullptr>
T f() { return 1; }

template<typename T
       , std::enable_if_t<std::is_same<T, unsigned long>::value>* = nullptr>
T f() { return 2; }

template<typename T
       , std::enable_if_t<std::is_same<T, size_t>::value
                      && !std::is_same<T, unsigned long>::value>* = nullptr>
T f() { return 3; }

因此,在coliru上似乎“
size\u t==unsigned long
”。

根据我的经验:不直接使用全局函数(在键入时阅读davidhigh的答案:好的,它工作,但正如他所说的,它的伸缩性不好)。只有在解析模板参数时出现“错误”时,SFINAE才起作用。由于C++允许函数模板仅是完全专用的,所以当编译器试图编译专门化时,没有“解决”。 但是,对于类,编译器允许进行部分专门化,您可以这样做,这样做的好处是您只需要对大小\u t使用SFINAE表达式(在这里使用mySize,因为我可以更改它):

使用任何其他typedef输出(由于明显的原因,不是int):


这里有一种方法有点奇怪,但很容易使用:

//using MyType = unsigned int;
using MyType = unsigned long;

unsigned long f2(MyType *,int) { return 1; }
size_t        f2(size_t *,...) { return 2; }

template <typename T>
auto f() -> decltype(f2(static_cast<T*>(0),0)) {
    T* p = 0;
    return f2(p,0);
}

int main()
{
    std::cout << f<MyType>() << "\n";
    std::cout << f<size_t>() << "\n";
}
//使用MyType=unsigned int;
使用MyType=unsignedlong;
无符号长f2(MyType*,int){return 1;}
size_t f2(size_t*,…){return 2;}
模板
自动f()->decltype(f2(静态转换(0),0)){
T*p=0;
返回f2(p,0);
}
int main()
{

std::cout我接受了@davidhigh的答案,因为我认为这是我所问问题的最合适的解决方案,然而,在我的实际代码中,我使用了不同的解决方案,为了防止它对其他人有帮助,我将在这里描述它

我的解决方案基于@immibis的一条评论,不幸的是,该评论后来被删除了。它类似于“使用预处理器不能轻松完成这项工作吗?”我意识到,实际上可以使用
climits
中的C
*\u MAX
宏,而且解决方案非常简单。多亏了@immibis

我对所有可能导致有符号和无符号变体冲突的类型应用了预处理器保护。这包括
size\u t
uintmax\u t
ssize\u t
ptrdiff\u t
intmax\u t

此外,正如@tbleher在他的评论中指出的,有时相同大小的标称类型可以是不同的真类型,所讨论的例子是
unsigned long
unsigned long
。事实上,在我当前的系统上
sizeof(unsigned long)==sizeof(unsigned long)==8,带符号的变体同上。尽管它们大小相同,但它们被视为不同的真类型,不会冲突

我的方法是首先为每个保证的不同类型定义一个函数,然后为“可冲突”类型定义一个概念顺序,然后逐步为大小(1)大于
[unsigned]long
和(2)的每个可冲突类型实例化一个定义不等于排序中较早的任何可冲突类型的大小

下面是一个演示:

#include <climits> // most integer limit macros, including SSIZE_MAX
#include <cstddef> // size_t, ptrdiff_t, [u]intmax_t
#include <cstdint> // SIZE_MAX, PTRDIFF_{MIN,MAX}, UINTMAX_MAX, INTMAX_{MIN,MAX}
#include <sys/types.h> // ssize_t
#include <cstdio>

// primary template
template<typename T> void f(void);

// declarations -- guaranteed not to cause conflicts; dups are allowed
template<> void f<unsigned char>(void);
template<> void f<unsigned short>(void);
template<> void f<unsigned int>(void);
template<> void f<unsigned long>(void);
template<> void f<unsigned long long>(void);
template<> void f<size_t>(void);
template<> void f<uintmax_t>(void);
template<> void f<char>(void);
template<> void f<short>(void);
template<> void f<int>(void);
template<> void f<long>(void);
template<> void f<long long>(void);
template<> void f<ssize_t>(void);
template<> void f<ptrdiff_t>(void);
template<> void f<intmax_t>(void);

int main(void) {
    f<unsigned char>();
    f<unsigned short>();
    f<unsigned int>();
    f<unsigned long>();
    f<unsigned long long>();
    f<size_t>();
    f<uintmax_t>();
    f<char>();
    f<short>();
    f<int>();
    f<long>();
    f<long long>();
    f<ssize_t>();
    f<ptrdiff_t>();
    f<intmax_t>();
    return 0;
} // end main()

// definitions -- must use preprocessor guard on conflictable types
template<> void f<unsigned char>(void) { std::printf("%d\n",1); }
template<> void f<unsigned short>(void) { std::printf("%d\n",2); }
template<> void f<unsigned int>(void) { std::printf("%d\n",3); }
template<> void f<unsigned long>(void) { std::printf("%d\n",4); }
template<> void f<unsigned long long>(void) { std::printf("%d\n",5); }
#if SIZE_MAX > ULLONG_MAX
template<> void f<size_t>(void) { std::printf("%d\n",6); }
#endif
#if UINTMAX_MAX > ULLONG_MAX && UINTMAX_MAX != SIZE_MAX
template<> void f<uintmax_t>(void) { std::printf("%d\n",7); }
#endif
template<> void f<char>(void) { std::printf("%d\n",8); }
template<> void f<short>(void) { std::printf("%d\n",9); }
template<> void f<int>(void) { std::printf("%d\n",10); }
template<> void f<long>(void) { std::printf("%d\n",11); }
template<> void f<long long>(void) { std::printf("%d\n",12); }
#if SSIZE_MAX > LLONG_MAX
template<> void f<ssize_t>(void) { std::printf("%d\n",13); }
#endif
#if PTRDIFF_MAX > LLONG_MAX && PTRDIFF_MAX != SSIZE_MAX
template<> void f<ptrdiff_t>(void) { std::printf("%d\n",14); }
#endif
#if INTMAX_MAX > LLONG_MAX && INTMAX_MAX != SSIZE_MAX && INTMAX_MAX != PTRDIFF_MAX
template<> void f<intmax_t>(void) { std::printf("%d\n",15); }
#endif
事实证明,在我的系统中,所有可冲突的类型实际上都与真正的类型
unsigned long
long
冲突


此解决方案的两个限制是,它只能用于具有相应的
*\u MAX
宏的类型,而不适用于浮点类型,因为预处理器不支持浮点算术和比较。

@immibis:预处理器如何知道
sizeof(size\t)=sizeof(无符号长)
?警告:即使
sizeof(size\u t)==sizeof(无符号长),不能保证它们是同一类型。<代码> siZeSt也可以是<代码>未签名的长long < /C> >。@ ImibIS预处理器只进行文本处理。它不知道底层文本的结构,因此不能对C++数据类型大小进行谓词。我想,如果定义了这些类型的大小。作为所有平台上的标准预处理器宏,您可以使用预处理器条件来比较它们,但我认为不存在这样的“标准宏”。实际上,我正在重新思考我之前的评论…字节计数
sizeof()
值对预处理器不可用,但最大值(和最小值)可用像
ULONG_MAX
SIZE_MAX
这样的宏应该是可用的,所以这些宏可能会工作…@immibis可能已经在这里找到了一个解决方案。@bgoldst我见过
\if
处理内置类型的大小,但是你是对的,
SIZE\u t
可能不是内置类型,而是typedef。对于大量的专门化因此,我会使用这个解决方案(并且只有当答案不会像OP中那样变得更复杂时才使用我的答案),所以+1。我会进一步将它包装在
名称空间细节中
,并使用另一个调用类成员的函数。有时我会解决困难的部分,并监督明显的-当然是全局函数调用
int main()
{
    std::cout<<f<int>()<<std::endl;
    std::cout<<f<unsigned long>()<<std::endl;
    std::cout<<f<size_t>()<<std::endl;
    std::cout<<f<unsigned long long>()<<std::endl;
}
1
2
2
1
#include <iostream>
#include <type_traits>
using namespace std;

typedef unsigned int mySize;

//default
template <class P, class dummy = void>
class T{
    public:
   static P f(){return 0;}
};

//int
template <>
class T<int,void> {
    public:
   static int f(){return 1;}
};

//unsigned long
template <>
class T<unsigned long, void> {
    public:
   static unsigned long f(){return 2;}
};

template <class P>
class T<P, typename enable_if<is_same<P, mySize>::value && !is_same<P, unsigned long>::value, void>::type> {
    public:
   static P f(){return 3;}
};

int main() {
    cout << T<int>::f() << endl;
    cout << T<unsigned long>::f() << endl;
    cout << T<mySize>::f() << endl;
    return 0;
}
1
2
2
1
2
3
//using MyType = unsigned int;
using MyType = unsigned long;

unsigned long f2(MyType *,int) { return 1; }
size_t        f2(size_t *,...) { return 2; }

template <typename T>
auto f() -> decltype(f2(static_cast<T*>(0),0)) {
    T* p = 0;
    return f2(p,0);
}

int main()
{
    std::cout << f<MyType>() << "\n";
    std::cout << f<size_t>() << "\n";
}
#include <climits> // most integer limit macros, including SSIZE_MAX
#include <cstddef> // size_t, ptrdiff_t, [u]intmax_t
#include <cstdint> // SIZE_MAX, PTRDIFF_{MIN,MAX}, UINTMAX_MAX, INTMAX_{MIN,MAX}
#include <sys/types.h> // ssize_t
#include <cstdio>

// primary template
template<typename T> void f(void);

// declarations -- guaranteed not to cause conflicts; dups are allowed
template<> void f<unsigned char>(void);
template<> void f<unsigned short>(void);
template<> void f<unsigned int>(void);
template<> void f<unsigned long>(void);
template<> void f<unsigned long long>(void);
template<> void f<size_t>(void);
template<> void f<uintmax_t>(void);
template<> void f<char>(void);
template<> void f<short>(void);
template<> void f<int>(void);
template<> void f<long>(void);
template<> void f<long long>(void);
template<> void f<ssize_t>(void);
template<> void f<ptrdiff_t>(void);
template<> void f<intmax_t>(void);

int main(void) {
    f<unsigned char>();
    f<unsigned short>();
    f<unsigned int>();
    f<unsigned long>();
    f<unsigned long long>();
    f<size_t>();
    f<uintmax_t>();
    f<char>();
    f<short>();
    f<int>();
    f<long>();
    f<long long>();
    f<ssize_t>();
    f<ptrdiff_t>();
    f<intmax_t>();
    return 0;
} // end main()

// definitions -- must use preprocessor guard on conflictable types
template<> void f<unsigned char>(void) { std::printf("%d\n",1); }
template<> void f<unsigned short>(void) { std::printf("%d\n",2); }
template<> void f<unsigned int>(void) { std::printf("%d\n",3); }
template<> void f<unsigned long>(void) { std::printf("%d\n",4); }
template<> void f<unsigned long long>(void) { std::printf("%d\n",5); }
#if SIZE_MAX > ULLONG_MAX
template<> void f<size_t>(void) { std::printf("%d\n",6); }
#endif
#if UINTMAX_MAX > ULLONG_MAX && UINTMAX_MAX != SIZE_MAX
template<> void f<uintmax_t>(void) { std::printf("%d\n",7); }
#endif
template<> void f<char>(void) { std::printf("%d\n",8); }
template<> void f<short>(void) { std::printf("%d\n",9); }
template<> void f<int>(void) { std::printf("%d\n",10); }
template<> void f<long>(void) { std::printf("%d\n",11); }
template<> void f<long long>(void) { std::printf("%d\n",12); }
#if SSIZE_MAX > LLONG_MAX
template<> void f<ssize_t>(void) { std::printf("%d\n",13); }
#endif
#if PTRDIFF_MAX > LLONG_MAX && PTRDIFF_MAX != SSIZE_MAX
template<> void f<ptrdiff_t>(void) { std::printf("%d\n",14); }
#endif
#if INTMAX_MAX > LLONG_MAX && INTMAX_MAX != SSIZE_MAX && INTMAX_MAX != PTRDIFF_MAX
template<> void f<intmax_t>(void) { std::printf("%d\n",15); }
#endif
1
2
3
4
5
4
4
8
9
10
11
12
11
11
11