静态断言模板类型 T 不完整?

9

有没有一种方法可以在头文件中静态断言类型T在该点完成?这个想法是,如果有人在不应该的地方添加#includes,就会产生编译错误。

相关: 如何编写“is_complete”模板?

使用该链接的答案,

namespace
{
template<class T, int discriminator>
struct is_complete {
  static T & getT();
  static char (& pass(T))[2];
  static char pass(...);
  static const bool value = sizeof(pass(getT()))==2;
};
}
#define IS_COMPLETE(X) is_complete<X,__COUNTER__>::value
class GType;
static_assert(!IS_COMPLETE(GType),"no cheating!");

不幸的是,这里出现了“无效使用不完整类型”的错误,唉。有没有一种方法来对否定进行断言?


3
由于您有C++11,所以您可以使用表达式SFINAE - chris
1
@chris:你为什么在那里引入declval?http://coliru.stacked-crooked.com/a/d2987f9901270a48 - Deduplicator
1
@Deduplicator,将S更改为struct S {S(int){}};,然后只需使用S{}而不是std::declval<S>()即可获得0std::declval会给你一个右值引用,因此您不需要使用可能存在或不存在的构造函数。但是,是的,一开始所有这些都是完全不必要的,因为我很蠢,sizeof可以用于类型。 - chris
基于此: clang++ -std=c++1y -stdlib=libc++ -Wall -Wextra -pedantic-errors -O3 -pthread main.cpp /usr/lib/x86_64-linux-gnu/libstdc++.so.6 && ./a.out #clang++ -E -P main.cpp #g++-4.9 -std=c++1y -Wall -Wextra -pedantic-errors -O3 -pthread main.cpp && ./a.out #clang -x c -std=c11 -Wall -Wextra -pedantic -O3 main.cpp && ./a.out 1 0从发布的链接来看,即使是GCC 4.7x,更不用说VS2012了,我也没有运气?好吧,我会把它归档到“将来使用”中! - peter karasev
2个回答

4
这里有一个基于chris建议使用表达式SFINAE的函数,可以检查类型是否已经完成。
我的采用不需要包含文件,在缺少所需参数时会出错(无法隐藏参数),适用于C++11及以后版本。
template<typename T>
constexpr auto is_complete(int=0) -> decltype(!sizeof(T)) {
    return true;   
}

template<typename T>
constexpr bool is_complete(...) {return false;}

还有一个测试套件:

struct S;

bool xyz() {return is_complete<S>(0);}

struct S{};

#include <iostream>
int main() {
    std::cout << is_complete<int>(0) << '\n';
    std::cout << xyz() << '\n';
    std::cout << is_complete<S>(0);
}

输出:

1
0
1

See live on coliru


当然可以只是制作一个包装器:constexpr bool foo() {return is_complete<T>(0);}。C++14将其简化为变量模板。 - chris
@chris:我试过了,但这样做会使第二次调用等于第一次... - Deduplicator
@MarcGlisse:我认为这不违反ODR,表达式SFINAE仅用于选择正确的重载。 - Deduplicator
constexpr是100%必要的吗?如果它可以在Visual Studio 2012中构建,那就太好了,因为我试图防止违反在此处放置编译器错误的概念。 - peter karasev
1
我认为在这个解决方案中它们不是可选的...除非你当然不需要编译时常量表达式。 - Deduplicator

2
通过...传递引用是无效的。
5.2.2/7:
当没有给定参数时,参数以一种方式传递,接收函数可以通过调用va_arg(18.10)获取参数的值。对参数表达式执行左值到右值(4.1)、数组到指针(4.2)和函数到指针(4.3)标准转换。具有(可能带有cv限定符的)类型std::nullptr_t的参数将转换为void*类型(4.10)。在这些转换之后,如果参数没有算术、枚举、指针、成员指针或类类型,则程序是非法的。
下面是从@chris的评论中匆忙调整的一种“有点工作”的解决方案:
#include <iostream>
#include <utility>
namespace
{

    template<typename T, int>
        constexpr auto is_complete(int) -> decltype(sizeof(T),bool{}) {
            return true;
        }

    template<typename T, int>
        constexpr auto is_complete(...) -> bool {
            return false;
        }
}


#define IS_COMPLETE(T) is_complete<T,__LINE__>(0) // or use __COUNTER__ if supported

struct S;

static_assert(IS_COMPLETE(int), "oops 1!");
static_assert(!IS_COMPLETE(S), "oops 2!");

struct S {};

static_assert(IS_COMPLETE(S), "oops 3!");

使用gcc 4.7.2出现错误:static_assert(!IS_COMPLETE(S), "oops 2!"); - peter karasev
你在本地尝试使用哪个编译器和标志? - peter karasev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接