Короткое замыкание в выражениях noException

учитывая следующий код:

// g++ main.cpp -std=c++23

#include <type_traits>

using namespace std;

template<typename P>
concept pointer_like = is_pointer_v<P> || requires (P p) { p.operator ->(); };

template<pointer_like P>
constexpr auto func(P p)
    noexcept(is_pointer_v<P> || noexcept(p.operator ->())) // error: 'int *' doesn't have 'operator ->'
{
    if constexpr (is_pointer_v<P>)
        return p;
    else
        return p.operator ->();
}

int main() {
    int i = 0;
    auto pi = func(&i); // error from here
}

https://godbolt.org/z/Gqq3W4o4h

Кажется, короткое замыкание не выполняется в выражении noexcept:

noexcept(is_pointer_v<P> || noexcept(p.operator ->()))

Мои ожидания:

  • Если P — указатель, is_pointer_v<P> истинно, поэтому operator -> не проверяется;
  • В противном случае проверьте, действителен ли operator ->.

Это что-то, чего не хватает в стандарте C++?

Обратите внимание, что фиксированным является только порядок вычисления операндов для ||. Оба операнда || должны быть допустимыми выражениями.

user12002570 01.09.2024 10:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
81
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Каждое подвыражение в noexcept должно быть действительным индивидуально/отдельно.

Вы можете использовать requires для достижения желаемого эффекта/результата, как показано ниже:

template<pointer_like P> constexpr auto func(P p)
    noexcept(requires { requires is_pointer_v<P> || noexcept(p.operator ->()); })
{
    if constexpr (is_pointer_v<P>)
        return p;
    else
        return p.operator ->();
}

Рабочая демо

Я не думаю, что это работает, потому что requires означает, что содержимое {...} действительно, но НЕ обязательно ИСТИНА. Пожалуйста, посмотрите тестовый код: godbolt.org/z/ea7Wja6M5.

DoZerg 01.09.2024 11:22

@DoZerg Вы используете старую демо-ссылку. Это работает, когда вы используете демо-версию новой ссылки. Обратите внимание на использование requires requires для выполнения этой работы, которого нет в вашей демо-версии.

user12002570 01.09.2024 11:33

Только что заметил! Спасибо за ответ!

DoZerg 01.09.2024 11:35

@DoZerg Пожалуйста :) Кстати, ваш опубликованный ответ на самом деле такой же, как и мой. Вы явно используете дополнительные круглые скобки, которые здесь не нужны, но могут быть использованы.

user12002570 01.09.2024 11:37

Выражение в noexcept должно быть правильно сформулировано. Сокращение логических операторов применимо только к оценке операндов, а не к их правильности.

Проблему можно решить с помощью другого уровня косвенности, который правильно обрабатывает неправильно сформированные операнды:

template<pointer_like P>
consteval auto noexcept_check() 
{
    if constexpr (is_pointer_v<P>)
        return true;
    else
        return noexcept(P{}.operator ->());
}

и тогда объявление func просто:

template<pointer_like P>
constexpr auto func(P p)
    noexcept(noexcept_check<P>()) 

демо

Спасибо за ответ! Это «стандартный» способ, который я много видел в STL, хотя мне интересно, существует ли более короткий подход.

DoZerg 01.09.2024 11:39

@DoZerg Короче говоря, вы имеете в виду отсутствие написания отдельной функции? Вы можете использовать эту технику с requires, как показано в другом ответе. Лично я предпочитаю именованную функцию, поскольку ее легче читать и более четко передать намерение (конечно, и то, и другое определенно субъективно), и ее можно использовать повторно.

cigien 01.09.2024 11:46

Другие вопросы по теме

Похожие вопросы

Как правильно использовать именованные аргументы в библиотеке C++ Fmt
Как выбрать перегрузку метода const при приведении указателя метода
Ошибка AddressSanitizer при использовании статической функции-члена в качестве компаратора в очереди приоритетов C++
GCC и MSVC принимают «alignas», тогда как Clang отклоняет его
Тернарный оператор не вычисляется во время компиляции — Метапрограммирование шаблонов
Как передать rvalue std::vector в функцию, принимающую std::span
Почему компилятор С++ хочет преобразовать в int, если тип объявлен неправильно?
Как устранить ошибку «неизвестный размер массива при удалении» при использовании класса с гибким элементом массива (FAM) и нетривиальным деструктором?
Функция мощности во встроенной 64-битной ассемблере на C++ Builder для базы с плавающей запятой
Какой родительский класс определяет функцию, переопределяемую ключевым словом override?