2016-12-30 11 views
16

Występuje błąd kompilatora, którego nie mogę wyjaśnić i nie udało mi się znaleźć informacji o nim w trybie online. Ostatnio dodałem specyfikator noexcept do destruktora klasy opakowującej, a teraz nie można skompilować dużej liczby klas dziedziczących po klasach korzystających z tego opakowania. Próbowałem go z GCC 4.9 bez błędu kompilatora.C2694 na destruktorze, gdy destruktor elementu członkowskiego klasy podstawowej zawiera niepustujący specyfikator wyjątków noexcept i ciało

Używam Visual Studio 2015 Profesjonalna wersja 14.0.25431.01 Update 3

Rozważmy następujący minimalny kod, który reprodukuje problem:

#include <type_traits> 

template<class T> 
struct member 
{ 
    ~member() noexcept(std::is_nothrow_destructible<T>::value) {}; 
}; 

struct parent 
{ 
    virtual ~parent() noexcept = default; 
}; 


struct derived : public parent 
{ 
    member<int> x; 
}; 

Fragment wytwarza się następujący komunikat o błędzie:

1>c:\users\fandrieux\workspace\tmp\dtor_noexcept\main.cpp(19): error C2694: 'derived::~derived(void) noexcept(<expr>)': overriding virtual function has less restrictive exception specification than base class virtual member function 'parent::~parent(void) noexcept' 
1> c:\users\fandrieux\workspace\tmp\dtor_noexcept\main.cpp(19): note: compiler has generated 'derived::~derived' here 
1> c:\users\fandrieux\workspace\tmp\dtor_noexcept\main.cpp(12): note: see declaration of 'parent::~parent' 

Co jest dla mnie interesujące to, że błąd kompilatora zniknął, jeśli zastąpiono ciało destruktora członka przez = default lub użyć noexcept lub noexcept(true):

// None of these produce compiler errors 
virtual ~member() noexcept(std::is_nothrow_destructible<T>::value) = default; 
virtual ~member() noexcept(true) {} 
virtual ~member() noexcept {} 

wiem, że to jest destruktor nie rzucać. Paranoids i sceptycy (jak ja) może dodać następujący statyczny assert i sprawdza się:

static_assert(std::is_nothrow_destructible<T>::value, "Might throw!"); 

According to MSDN sygnalizuje niewystarczającą dynamikę specyfikatora wyjątku. Jak to się tutaj stosuje? Czy nie ma żadnego wyjątku ([wyrażenie logiczne]) równoważnego z noexcept(true) lub noexcept(false)? Dlaczego zmienia się to w zależności od obecności ciała funkcyjnego? Dodanie jawnego noexcept destructor do wyprowadzonego powoduje błąd kompilatora, ale wydaje się to niepotrzebnym obejściem. W praktyce jest to również duże obciążenie, gdy weźmie się pod uwagę, że każda klasa pochodna musiałaby zostać zaktualizowana.

+1

Niech upvotes mówią same za siebie, ale to jest świetne pytanie. Dziękuję i powodzenia – Tas

+1

Fwiw, oprócz gcc 4.9, to również przechodzi przez zbiórki przez clang 3.8 w języku C++ 14 bez żadnych problemów. – WhozCraig

+0

Wygląda jak błąd kompilatora. –

Odpowiedz

6

Wygląda to na błąd kompilatora. Jeśli dodamy następujące klasy:

struct dtor_throws 
{ 
    ~dtor_throws() noexcept(false) {} 
}; 

i zmienić definicję derived wygląda następująco:

struct derived : public parent 
{ 
    member<dtor_throws> x; 
}; 

Następnie GCC i Clang zarówno skarżą się, że specyfikacja wyjątek od ~derived jest luźniejsza niż ~parent.

W oryginalnym przykład pojawia MSVC nie być osadzanie wartość wyrażenia noexcept w rodzaju ~parent, lecz jedynie traktując wszystkich mieszanek noexcept specyfikacje zdefiniowane przez użytkownika destruktorów szablonów klasy jako bardziej zrelaksowany niż noexcept(true) .

MSVC 2017 RC ma również wpływ.

Powiązane problemy