2017-05-13 1 views
11

아침, 여러분!constexpr 고유 ID, clang으로 컴파일되지만 gcc로 컴파일되지 않음

이벤트 대기열을 리팩토링하고 있습니다. 컴파일 할 때 이벤트 ID를 고유하게 만들 수 있는지 살펴 보려고합니다. 내가 clang 4.0.0에서 작동하지만 g ++ 6.3.1에서 컴파일 오류가 발생합니다.

아이디어는 정적 멤버 변수의 주소를 사용하여 개별 유형을 고유하게 식별 한 다음 태그 지정을 사용하여 클래스 템플릿에서 이러한 고유 한 유형을 생성하는 것입니다.

정적 멤버의 주소를 유형 ID로 사용하는 것은 비교적 일반적인 기술이지만 템플릿을 사용하면 ODR을 알 수 없다는 것을 의미합니다. MSN은 이것이 유효한 접근 방법임을 제안하기 위해 여기에 표준을 인용했습니다. Compile-time constant id

제 문제는이 constexpr입니다. constexpr을 제거하고 이것을 런타임에 테스트하면 모든 것이 예상대로 작동합니다. 그러나이 constexpr을 수행하면 g ++에서 정적 어설 션이 실패합니다. "error : 정적 어설 션에 대한 상수가 아닌 조건"입니다.

이러한 문제의 대부분은 g ++이 부적합하고 clang ++ 오류입니다. 이것은 그 반대입니다.

내가 곤혹 스럽다. 여기에 정적에 ++ g에 컴파일되지 않습니다 어떤 주석 내가 뭘 찾았는지의 제거 다운 버전이의 주장 :

~$ clang++ --version 
clang version 4.0.0 (tags/RELEASE_400/final) 
Target: x86_64-unknown-linux-gnu 
Thread model: posix 
InstalledDir: /usr/bin 
~$ clang++ -std=c++14 -c example.cpp 
~$ g++ --version 
g++ (GCC) 6.3.1 20170306 
Copyright (C) 2016 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

example.cpp:14:1: error: non-constant condition for static assertion 
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++."); 
^~~~~~~~~~~~~ 
example.cpp:14:34: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression 
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++."); 
       ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ 
example.cpp:15:1: error: non-constant condition for static assertion 
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this."); 
^~~~~~~~~~~~~ 
example.cpp:15:15: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression 
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this."); 
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
example.cpp:19:1: error: non-constant condition for static assertion 
static_assert(id_0 != id_1, "This also does not."); 
^~~~~~~~~~~~~ 
example.cpp:19:20: error: ‘((& t<tag_0>::storage) != (& t<tag_1>::storage))’ is not a constant expression 
static_assert(id_0 != id_1, "This also does not."); 
       ~~~~~^~~~~~~ 
~$ 

나 ': 여기

template <typename tag> 
struct t 
{ 
    constexpr static char const storage{}; 
}; 
template <typename tag> 
constexpr char const t<tag>::storage; 

struct tag_0 {}; 
struct tag_1 {}; 

static_assert(&t<tag_0>::storage == &t<tag_0>::storage, "This always compiles."); 
static_assert(&t<tag_1>::storage == &t<tag_1>::storage, "So does this."); 
static_assert(&t<tag_0>::storage != &t<tag_1>::storage, "This does not compile with g++."); 
static_assert(!(&t<tag_0>::storage == &t<tag_1>::storage), "Neither does this."); 

constexpr auto id_0 = &t<tag_0>::storage; // This does. 
constexpr auto id_1 = &t<tag_1>::storage; // This does. 
static_assert(id_0 != id_1, "This also does not."); 

그리고 컴파일러 출력입니다 내가 왜 constexpr을 이해하는지와 충돌하기 때문에이 특정 접근법이 gcc로 컴파일되지 않는 이유가 궁금하다.

(이 좋은 디자인의 경우 내가 물어 아니에요, 또는 이러한 목표를 달성 할 수있는 다른 방법이 있는지. 나는이 작업을 수행 할 수있는 다른 방법이있다.)

감사합니다!

편집 : 두 컴파일러로 컴파일 않습니다 템플릿이없는 비교 예는 다음과 같을 수 있습니다 당신은 constexpr 반원에 두 가지 포인터를 비교하기 위해 노력하고 있으며 표준에 지정되지 않은

struct t1 
{ 
    static constexpr int const v{}; 
}; 
constexpr int t1::v; 

struct t2 
{ 
    static constexpr int const v{}; 
}; 
constexpr int t2::v; 

static_assert(&t1::v != &t2::v, "compiles with both"); 
+0

g ++ 7.1.1에서도 마찬가지입니다. –

답변

0

, 컴파일러가 constexpr (객체 주소가 아직 알려지지 않았을 수도 있습니다)으로 평가해야하는지 여부. MSVC와 clang은 그렇게하며 gcc는 그렇지 않습니다. 자체에 대한 포인터의 비교 결과가 정의됩니다.

+1

그들은 회원이 아닙니다.변수는 정적입니다. 여기에 표준 사이트를 배치 할 수 있습니까? 이것은 조금 비합리적으로 들립니다. –

+0

@Frank Secilia 그들은 여전히 ​​정적 멤버입니다. 이것은 전역 변수와 같지 않습니다 (이 경우에는 정상적으로 작동합니다). 메소드에 대한 포인터 비교를 시도하면 동일하게 발생합니다. 표준에서는이 경우에 어떤 일이 발생하는지 정의하지 않았으므로 약간 모호합니다. – Swift

+0

@Swift 전역 변수 템플릿과 동일한 문제 : [demo] (https://wandbox.org/permlink/QlCuXt1Or0yreg3h). – Oktalist

관련 문제