2016-08-09 8 views
3

Почему компилятор C++ позволяет объявить функцию как constexpr, которая не может быть constexpr?Почему компилятор C++ позволяет объявить функцию как constexpr, которая не может быть constexpr?

Например: http://melpon.org/wandbox/permlink/AGwniRNRbfmXfj8r

#include <iostream> 
#include <functional> 
#include <numeric> 
#include <initializer_list> 

template<typename Functor, typename T, size_t N> 
T constexpr reduce(Functor f, T(&arr)[N]) { 
    return std::accumulate(std::next(std::begin(arr)), std::end(arr), *(std::begin(arr)), f); 
} 

template<typename Functor, typename T> 
T constexpr reduce(Functor f, std::initializer_list<T> il) { 
    return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f); 
} 

template<typename Functor, typename T, typename... Ts> 
T constexpr reduce(Functor f, T t1, Ts... ts) { 
    return f(t1, reduce(f, std::initializer_list<T>({ts...}))); 
} 

int constexpr constexpr_func() { return 2; } 

template<int value> 
void print_constexpr() { std::cout << value << std::endl; } 

int main() { 
    std::cout << reduce(std::plus<int>(), 1, 2, 3, 4, 5, 6, 7) << std::endl; // 28 
    std::cout << reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7}) << std::endl;// 28 

    const int input[3] = {1, 2, 3}; // 6 
    std::cout << reduce(std::plus<int>(), input) << std::endl; 

    print_constexpr<5>(); // OK 
    print_constexpr<constexpr_func()>(); // OK 
    //print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error 

    return 0; 
} 

Выход:

28 
28 
6 
5 
2 

Почему ошибка в этой строке: //print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // errorдаже для C++ 14 и 1z C++?

Почему компилятор позволяет отмечать reduce() как constexpr, но reduce() не может использоваться в качестве параметра шаблона даже если все параметры переданы в reduce(), известных во время компиляции?


Тот же эффект для некоторых составителей - который поддерживает C++ 14-std=c++14:

Для всех этих случаев компилировать OK, пока неиспользованного линии: //print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error

+3

" _the C++ compiler_ "? Какой компилятор? GCC? Clang? MSVC? Какая версия? –

ответ

4

Давайте идти прямо от его предложения, www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235. PDF в разделе 4.1, третий абзац: я цитирую:

функция постоянной экспрессии может быть вызвана с непостоянным выражений, в этом случае не требуется, чтобы в результате значение будет вычислено во время компиляции ,

Смотрите этот вопрос: When does a constexpr function get evaluated at compile time?

template<typename Functor, typename T> 
T constexpr reduce(Functor f, std::initializer_list<T> il) { 
    return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f); 
} 

Опять же, как вы знаете, std::accumulate не constexpr функция.

template<int value> 
void print_constexpr() { std::cout << value << std::endl; } 

Опять же, как известно, non-type template arguments must be constant expressions.


Сейчас:

template<typename Functor, typename T> 
T constexpr reduce(Functor f, std::initializer_list<T> il) { 
    return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f); 
} 

Как, почему это работает: Вот то, что стандарт C++ должен сказать:

[dcl.constexpr/6] (курсив мой):

Если заданная шаблонная специализация функции constexpr templ ели или функции члена шаблона класса не сможет удовлетворить requirements for a constexpr function или constexpr конструктор, , что специализация еще constexpr функция или constexpr конструктор, даже если вызов такой функции может не появиться в константным выражением ...

Примечание: that

функция экземпляра из функции Templa te называется функцией специализация шаблона;


Когда его не шаблон, это не получится:

int constexpr reduce(int(*f)(int, int), std::initializer_list<int> il) { 
    return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f); 
} 

Компилятор будет жаловаться теперь, что вы не можете вызвать не- constexpr функцию в функции, определенной в constexpr

3

Если написать это код:

constexpr int result = reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7}); 

вы увидите, что уменьшение не дает результата constexpr.

Причина в том, что «Примечание: не-constexpr функция„аккумулировать>“не может быть использовано в постоянном выражении» И как вы можете видеть здесь - http://en.cppreference.com/w/cpp/algorithm/accumulate

станд :: скапливаются не constexpr

EDIT расширяется, чтобы ответить на фактический вопрос, спасибо @peterchen: Он скомпилирован, когда он обращается к использованию, он не пытается и не может попытаться решить эту проблему до тех пор, пока не скомпилирует определенную версию шаблона. Когда он попадает в использование и запускает компиляцию, он решает проблему накопления и видит, что это не constexpr, поэтому выдает ошибку.

+2

Спасибо! Да, я знаю его. Но почему компилятор C++ позволяет объявить функцию как constexpr 'reduce()' - если она никогда не может быть constexpr? – Alex

+1

Он компилируется, когда он попадает в использование, он не пытается и не может попытаться разрешить эту функцию, пока не скомпилирует определенную версию шаблона. Когда он попадает в использование и запускает компиляцию, он решает проблему накопления и видит, что это не constexpr, поэтому выдает ошибку. –

+1

@TheSombreroKid: это ключевой момент - возможно, вы должны добавить его в свой ответ. – peterchen

3

Почему компилятор C++ позволяет объявлять функцию как constexpr, которая не может быть constexpr?

Это не так. Но вы не определяете функцию constexpr. Вы определяете шаблон.

Давайте установим:

struct Is_constexpr { 
    constexpr Is_constexpr() = default; 
    constexpr auto bar() { 
    return 24; 
    } 
}; 

struct Not_constexpr { 
    auto bar() { 
    return 24; 
    } 
}; 

Теперь, если вы пытаетесь определить функцию (не шаблон) в качестве constexpr, который использует Not_constexpr компилятор не позволит вам:

constexpr auto foo_function(Not_constexpr v) 
{ 
    return v.bar(); 
    // error: call to non-constexpr function 'auto Not_constexpr::bar()' 
} 

Вы однако определяя шаблон. Посмотрим, как это получится:

template <class T> 
constexpr auto foo(T v) 
{ 
    return v.bar(); 
} 

Компилятор позволяет это сделать. Нет ошибки. Зачем? Потому что это шаблон. Некоторые конкретизации могут быть constexpr, некоторые нет, в зависимости от T:

int main() { 
    constexpr Is_constexpr is_c; 
    constexpr Not_constexpr not_c; 

    std::integral_constant<int, foo(is_c)> a; // OK 
    //std::integral_constant<int, foo(not_c)> a; // ERROR 

} 

 Смежные вопросы

  • Нет связанных вопросов^_^