On-line: гостей 1. Всего: 1 [подробнее..]
Программисты всех стран, объединяйтесь!

АвторСообщение



ссылка на сообщение  Отправлено: 23.11.18 16:55. Заголовок: Неоднозначность синтаксиса в C++. Как круглые скобки вокруг аргумента влияют на выбор шаблона.


В C++ есть такие "темные места", которые интуитивно не ясны, и такие возможности языка следует просто знать.

Одно из таких "темных мест" связано с аргументами шаблонов. Имеется неоднозначность в интерпретации конструкций вида T(), где T - это некоторый тип, при использовании их в качестве аргументов шаблона. С одной стороны, такая конструкция может рассматриваться как задание типа (type-id), а, с другой стороны, как задание выражение.

C++ независимо от контекста использования аргумента шаблона всегда рассматривает такую конструкцию как задание типа (type-id).

Согласно параграфу №2 раздела 17.3 Template arguments

 цитата:
2 In a template-argument, an ambiguity between a type-id and an expression is resolved to a type-id, regardless
of the form of the corresponding template-parameter.


Например, в следующей демонстрационной программе будет вызвана функция с шаблонным параметром типа
 
#include <iostream>

template <typename>
void f()
{
std::cout << "The function with a type template parameter is called.\n";
}

template <int>
void f()
{
std::cout << "The function with a non-type template parameter is called.\n";
}


int main()
{
f<int()>();
}

Вывод программы на консоль будет
 
The function with a type template parameter is called.

А как в таком случае, используя конструкцию вида T(), все же вызвать шаблонную функцию, которая имеет шаблонный параметр, не являющийся типом, то есть чтобы использовать эту конструкцию как выражение?

Для этого достаточно заключить эту конструкцию в круглые скобки, и тогда однозначно получится выражение. Например,
 
#include <iostream>

template <typename>
void f()
{
std::cout << "The function with a type template parameter is called.\n";
}

template <int>
void f()
{
std::cout << "The function with a non-type template parameter is called.\n";
}


int main()
{
f<int()>();
f<( int() )>();
}

Вывод программы на консоль будет
 
The function with a type template parameter is called.
The function with a non-type template parameter is called.

Обратите внимание на то, что теперь, согласно стандарту C++ 17, вторая шаблонная функция также может быть определена, используя спецификатор auto в качестве объявления шаблонного параметра.
Ниже приведена соответствующая демонстрационная программа.
 
#include <iostream>

template <typename>
void f()
{
std::cout << "The function with a type template parameter is called.\n";
}

template <auto>
void f()
{
std::cout << "The function with a non-type template parameter is called.\n";
}


int main()
{
f<int()>();
f<( int() )>();
}

Вывод этой демонстрационной программы аналогичен выводу предыдущей демонстрационной программы
 
The function with a type template parameter is called.
The function with a non-type template parameter is called.


Спасибо: 0 
ПрофильЦитата Ответить
Ответов - 1 [только новые]





ссылка на сообщение  Отправлено: 19.06.19 13:47. Заголовок: Вот еще один пример ..


Вот еще один пример на неоднозначность синтаксических конструкция в C++.

Рассмотрим следующую программу.

 
#include <iostream>
#include <memory>

int main()
{
std::shared_ptr<int> p( new int( 42 ) );

std::cout << "#1: " << p.use_count() << '\n';

{
std::cout << "#2: " << p.use_count() << '\n';

std::shared_ptr<int>( p );

std::cout << "#3: " << p.use_count() << '\n';
}

std::cout << "#4: " << p.use_count() << '\n';
}

Спрашивается, какие значения use_count() программа выведет на консоль.

Сложность ответа на вопрос может вызывать наличие конструкции

  
std::shared_ptr<int>( p );

во внутреннем блоке кода функции main. То есть как эта конструкция влияет на вывод?

Согласно стандарту C++ (8.5.1.3 Explicit type conversion (functional notation))

 цитата:
1 A simple-type-specifier (10.1.7.2) or typename-specifier (17.7) followed by a parenthesized optional expressionlist or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer....



То есть согласно этой цитате данное предложение
  
std::shared_ptr<int>( p );

представляет собой предложение выражения, содержащее выражение явного (функционального) приведения типов.

Это предложение никак не может повлиять на вывод значения p.use_count() в последующих предложениях.

Но, с другой стороны, данное предложение можно рассматривать как объявление локальной переменной p, так как деклараторы можно заключать в круглые скобки.

Действительно, например, следующее предложение

int ( x );

является корректным объявлением переменной x, имеющей тип int. Таким образом предложение

  
std::shared_ptr<int>( p );

является объявлением локальной переменной p, имеющей тип std::shared_ptr<int>.
Для наглядности можно спецификатор типа и декларатор разделить дополнительным пробелом
  
std::shared_ptr<int> ( p );

Как в C++ разрешается такая неоднозначноть? Согласно разделу 9.8 Ambiguity resolution

 цитата:
1 There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (8.5.1.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration



Итак, стандарт разрешает эту неоднозначность в пользу объявления переменной.

Следовательно в программе во внутреннем блоке объявляется новая локальная переменная p, которая скрывает одноименную переменную p, объявленную во внешнем блоке, используя конструктор по умолчанию

constexpr shared_ptr() noexcept;

Согласно описанию этого конутрктора

 цитата:
2 Effects: Constructs an empty shared_ptr object.
3 Postconditions: use_count() == 0 && get() == nullptr


Учитывая все сказанное, вывод программы на консоль будет выглядеть следующим образом:
 
#1: 1
#2: 1
#3: 0
#4: 1

То есть в третьем предложении вывода в поток будет выведено на консоль значение use_count, равное 0 для нового только что созданного локального объекта p.

А как это предложение с объявлением превратить в предложение выражения?

Достаточно просто заключить тело предложения в круглые скобки, как показано в программе ниже.
 
#include <iostream>
#include <memory>

int main()
{
std::shared_ptr<int> p( new int( 42 ) );

std::cout << "#1: " << p.use_count() << '\n';

{
std::cout << "#2: " << p.use_count() << '\n';

( std::shared_ptr<int>( p ) );

std::cout << "#3: " << p.use_count() << '\n';
}

std::cout << "#4: " << p.use_count() << '\n';
}

И тогда вывод на консоль уже будет выглядеть следующим образом:
 
#1: 1
#2: 1
#3: 1
#4: 1


Альтернативный подход, если не использовать круглые скобки, состоит в том, чтобы записать выражение явного функционального приведения типа с использованием фигурных скобок. Ниже показано, как будет выглядеть соответствующее предложение.

 
#include <iostream>
#include <memory>

int main()
{
std::shared_ptr<int> p( new int( 42 ) );

std::cout << "#1: " << p.use_count() << '\n';

{
std::cout << "#2: " << p.use_count() << '\n';

std::shared_ptr<int>{ p };

std::cout << "#3: " << p.use_count() << '\n';
}

std::cout << "#4: " << p.use_count() << '\n';
}

Вывод на консоль будет таким же, как показано выше
 
#1: 1
#2: 1
#3: 1
#4: 1


Это сообщение я создал на основе вопроса на Stackoverflow Understanding C++ std::shared_ptr when used with temporary objects и моего ответа на него.

Спасибо: 0 
ПрофильЦитата Ответить
Ответ:
1 2 3 4 5 6 7 8 9
большой шрифт малый шрифт надстрочный подстрочный заголовок большой заголовок видео с youtube.com картинка из интернета картинка с компьютера ссылка файл с компьютера русская клавиатура транслитератор  цитата  кавычки моноширинный шрифт моноширинный шрифт горизонтальная линия отступ точка LI бегущая строка оффтопик свернутый текст

показывать это сообщение только модераторам
не делать ссылки активными
Имя, пароль:      зарегистрироваться    
Тему читают:
- участник сейчас на форуме
- участник вне форума
Все даты в формате GMT  3 час. Хитов сегодня: 12
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет