2014-03-28 2 views
6

thread_local과 함께 이상한 행동을하고 있고, 내가 잘못하고 있는지, GCC 버그인지 잘 모르겠습니다. 나는 다음과 같은 최소한의 생식 시나리오가 : 출력 위의 주석 라인thread_local 멤버 변수 구축

#include <iostream> 

using namespace std; 

struct bar { 
    struct foo { 
     foo() { 
      cerr << "foo" << endl; 
     } 
     int i = 42; 
    }; 

    static thread_local foo FOO; 
}; 

static thread_local bar::foo FREE_FOO; 
thread_local bar::foo bar::FOO; 

int main() { 
    bar b; 
    cerr << "main" << endl; 
    // cerr << FREE_FOO.i << endl; 
    cerr << b.FOO.i << endl; 
    return 0; 
} 

은 다음과 같습니다

:

main 
0 
주석 그것으로

Ideone

, 그것은이된다

main 
foo 
foo 
42 
42 

Ideone

여기에 바보가 있습니까?

$ gcc -v 
Using built-in specs. 
COLLECT_GCC=gcc 
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper 
Target: x86_64-linux-gnu 
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.8.1-10ubuntu9' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu 
Thread model: posix 
gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9) 

업데이트 :이뿐만 아니라 예기치 않은 결과를 제공

:

#include <iostream> 

using namespace std; 

template<class T> 
struct bar { 
    struct foo { 
     foo() { 
      cerr << "bar::foo" << endl; 
     } 
     int i = 42; 
    }; 

    void baz() { 
     cerr << bar::FOO.i << endl; 
    } 

    static thread_local foo FOO; 
}; 

struct far { 
    struct foo { 
     foo() { 
      cerr << "far::foo" << endl; 
     } 
     int i = 42; 
    }; 

    void baz() { 
     cerr << far::FOO.i << endl; 
    } 

    static thread_local foo FOO; 
}; 

template<class T> thread_local typename bar<T>::foo bar<T>::FOO; 
thread_local typename far::foo far::FOO; 

int main() { 
    cerr << "main" << endl; 
    bar<int> b; 
    b.baz(); 

    far f; 
    f.baz(); 
    return 0; 
} 

결과 : 내가 돈 불구하고

main 
0 
far::foo 
bar::foo 
42 
+0

혼란에 추가하는 코드입니다 : FOO가 예상대로 작동합니다 는 [Ideone] (http://ideone.com/QnIEXp) –

+1

참고 그 'static thread_local bar :: foo FREE_FOO;'에'static'은 단지 거기의 연결을 수정하고있을 때 효과가 없습니다 (기본값은 internal). 그것을 제거하면 동일한 동작을합니다. – Andy

+0

간접적으로 액세스되는 템플릿 클래스의 멤버도 초기화되지 않은 상태로 유지되는 반면 템플릿이 아닌 클래스 멤버에 대한 동일한 액세스는 초기화를 트리거합니다. [Ideone] (http://ideone.com/3mBIoO) –

답변

5

이 댓글 너무 긴 ' 그것을 완전히 이해한다고 주장하지 마십시오.

난 당신이 Coliru

#include <iostream> 
using namespace std; 

struct foo { 
    int i; 
    foo() : i{42} {} 
}; 

struct bar { 
    static thread_local foo FOO; 
}; 

thread_local foo bar::FOO; 

int main() { 
    //cerr << string((bar::FOO.i == 42) ? "Ok" : "Bug") << endl; //Ok 
    cerr << string((bar().FOO.i == 42) ? "Ok" : "Bug") << endl; //Bug 
} 

에서 실행할 수있는 나는 버그가 FOO 여부를 결정하려고하는이 시점 GCC에서이 GCC 소스 파일

https://chromium.googlesource.com/native_client/nacl-gcc/+/upstream/master/gcc/cp/decl2.c

에 생각 짧은 버전 는 정적 멤버 인 bar이며 초기화 된 경우 검색하는 래퍼 함수가 필요합니다 ... 래퍼가 필요하지 않은 것으로 결정됩니다. 이는 잘못된 것입니다. 확인합니다

  1. error_operand_p가 아닌가요? 예, 그렇지 않습니다. (아마도)
  2. thread_local (DECL_THREAD_LOCAL_P)입니까? 예, thread_local입니다.
  3. gnu __thread 확장 (DECL_GNU_TLS_P)이 아닌가요? 예, 그렇지 않습니다.
  4. 함수 범위 (DECL_FUNCTION_SCOPE_P)에서 선언되지 않았습니까? 예, 그렇지 않습니다.
  5. 변수가 다른 번역 단위 (TU)에 정의되어 있지 않습니까? 예, 그렇지 않습니다. (버그?)
  6. 소소한 소멸자가 없습니까? 예, 그렇지 않습니다.
  7. 초기화 프로그램이 없거나 상수 프로그램이 있습니까? 초기화 도구가 있지만 상수입니다.제대로 수행하지 못하면 이니셜 다음 일정 인 경우 동적으로 초기화되지 않는다는 결론

    1. , 또는
    2. :
    3. 그것은 결함 중 하나입니다

    을 래퍼를 필요로하지 않는다 정적 초기화 또는

  8. 구성원 변수 임에도 불구하고 외부에서 정의 할 수 없음을 알리지 못함

초기화가 생성자에 의해 수행 되었기 때문에 이것이 혼란의 원인이며 생성자가 호출되지만 값은 상수라고 생각합니다.

여기 바 b.Foo을 대체

/* Returns true iff we can tell that VAR does not have a dynamic 
    initializer. */ 

static bool 
var_defined_without_dynamic_init (tree var) 
{ 
    /* If it's defined in another TU, we can't tell. */ 
    if (DECL_EXTERNAL (var)) 
     return false; 
    /* If it has a non-trivial destructor, registering the destructor 
     counts as dynamic initialization. */ 
    if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (var))) 
     return false; 
    /* If it's in this TU, its initializer has been processed. */ 
     gcc_assert (DECL_INITIALIZED_P (var)); 
    /* If it has no initializer or a constant one, it's not dynamic. */ 
     return (!DECL_NONTRIVIALLY_INITIALIZED_P (var) 
      || DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var)); 
} 

/* Returns true iff VAR is a variable that needs uses to be 
    wrapped for possible dynamic initialization. */ 

static bool 
var_needs_tls_wrapper (tree var) 
{ 
    return (!error_operand_p (var) 
      && DECL_THREAD_LOCAL_P (var) 
      && !DECL_GNU_TLS_P (var) 
      && !DECL_FUNCTION_SCOPE_P (var) 
      && !var_defined_without_dynamic_init (var)); 
} 
+0

발견자를 버그 [report] (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60702)에 추가 할 수 있다면 좋을 것입니다. 감사! –

+1

@AnomanderRake clang은 또한 여러분이 만든 짧은 테스트와 실패한 테스트에 실패했습니다. 나는 그것을 clang 팀에 제출했으며, 최근에 수정 된 버그가 중복 된 것으로 판명되었습니다. 이 포인터. bugreport를 수정하여 고정 된 clang 버그를 가리 키도록하겠다. Richard Smith가 그 영향에 대한 코멘트를 남겼다는 것을 알았다. – amdn

+1

@AnomanderRake 귀하의 gcc 버그 보고서를 업데이트했습니다. 더 짧은 테스트 케이스를 추가하고 gcc 팀에게 clang 수정을 알려주었습니다. 제 생각보다 그것이 내 생각보다 생산적이라고 생각합니다. – amdn