All streams
Search
Write a publication
Pull to refresh
16
0
Send message
Для GCC вы можете делать как-то так:
#include <iostream>

#define PROGMEM

template <char... String>
struct ProgmemString {
    static const char PROGMEM v[sizeof...( String ) + 1];
};

template <char... String>
const char ProgmemString<String...>::v[sizeof...( String ) + 1] = {String..., '\0'};

template <typename CharT, CharT... String>
constexpr auto operator"" _progmem()
{
    return ProgmemString<String...>::v;
}

int main()
{
    std::cout << "progmem string"_progmem << std::endl;
}
Минусы: больший объём памяти, необходимый для выполнения.

А теперь я предлагаю вам подтвердить своё утверждение результатами тестов, где ваш сравниваемый код на javascript требует для выполнения меньше памяти.
Оператор простого присвоения нельзя определить вне класса. Впрочем для остальных операторов правило не меняется, при объявлении вне класса стоит возвращать референс на первый аргумент, а не значение.
Корректные операторы присвоения должны возвращать референс на *this, а не значение. Это де-факто стандартное ожидаемое поведение.
Реализуя Linux версию, мы сразу решили, что обязательно надо поддержать отслеживание запусков компилятора. Это поможет программистам быстрее и проще знакомиться с анализатором PVS-Studio. Дело в том, что, используя эту утилиту, проект сможет проверить любой член команды, не отвлекая людей, занимающихся поддержкой makefile-ов и вообще системы сборки. В больших проектах далеко не каждый знает, как собственно собирается их приложение. Тем более, не каждый найдет время и желание разбираться, как и куда прописать вызов PVS-Studio. Всё это ещё может быть усложнено наличием автогенерируемого makefile. Понятно, что во всём можно разобраться, но это является существенным барьером на пути первой проверки проекта и удовлетворения исследовательского любопытства.

Сомнительная функция, допустим у меня в makefile что-то такое:
.build/.obj/%.o: src/%.cpp .build/.obj/%.d
#   ...
    something $< | $(CXX) -o $@ $(CXXFLAGS) -


Тем более, что сборка часто запускается в много потоков, отжирая все 100% ресурсов машины. В таких условиях сложно синхронизировать параллельную работу компилятора и анализатора. А исходники к тому же могут быть как временными файлами, так и просто потоками.

Самым универсальным способом для любой системы сборки была бы замена компилятора, например:
CXX="pvs g++" make

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

Впрочем standalone версия, без запуска компиляции, тоже была бы полезна. Я бы, например, использовал анализатор как дополнительный шаг сборки в пререквизитах для компиляции.

Ну и совсем шикарно если бы вы учли возможность кросс-компиляции. У существующих анализаторов с этим как-то не очень.

Если дело пойдет, буду агитировать за внедрение вашего продукта у нас. Удачи!
Я хотел бы видеть этот список на сайте до того, как я впустую потрачу время на регистрацию (или не потрачу, а сразу уйду к конкурентам).
Свои документы составляю на LaTeX, чужие смотрю тем, что есть под рукой: Libre Office или Google Docs.
Небольшая поправочка:
namespace detail {

template <class ...> struct match;

template <class Head, class ... Tail>
struct match<Head, Tail...> : match<Tail...>, Head {
    template <class Head2, class ... Tail2>
    match(Head2&& head, Tail2&& ... tail) : match<Tail...>(std::forward<Tail2>(tail)...), Head(std::forward<Head2>(head)) {}
    using Head::operator();
    using match<Tail...>::operator();
};

template <class T>
struct match<T>: T {
    template <class R>
    match(R&& r) : T(std::forward<R>(r)) {}
    using T::operator();
};

}

template <class T, class ... Cases>
decltype(auto) match(T&& value, Cases&& ... cases) {
    return detail::match<typename std::decay<Cases>::type...>{std::forward<Cases>(cases)...}(std::forward<T>(value));
}

Можно покороче:
namespace detail {

template <class ...> struct match;

template <class Head, class ... Tail>
struct match<Head, Tail...> : match<Tail...>, Head {
    template <class Head2, class ... Tail2>
    match(Head2&& head, Tail2&& ... tail) : match<Tail...>(std::forward<Tail2>(tail)...), Head(std::forward<Head2>(head)) {}
    using Head::operator();
};

template <> struct match<> {};

}


template <class T, class ... Cases>
decltype(auto) match(T&& value, Cases&& ... cases) {
	return detail::match<typename std::decay<Cases>::type...>{std::forward<Cases>(cases)...}(std::forward<T>(value));
}
Это не иероглифы, а хангыль: 수호신 — собственно так и читается «suhosin»
Извините, я должен был навечно зависнуть на этой строке, и не добраться до комментариев.
Если поменять NRVO на RVO будут делать и другие компиляторы:

M func( int Value )
{
    if ( Value > 0 ) {
        return M{100};
    }
    else {
        return M{-100};
    }
}

И резуьтат работы программы будет несколько отличным от приведенного в статье:

M(100)

M(-100)

2
~M()
~M()

Вывод который сделал автор о работе программы не имеет отношения к copy elision. И на его компиляторе оптимизации не происходило.
Извините, но компилятор всё делает правильно, это вы где-то запутались.

Что мы тут видим? А видим мы боль. Компилятор проигнорировал тот факт, что конструкторы и деструкторы M имеют глобальные побочные эффекты. Умный компилятор выбросил из нашей логической цепочки:

— создание копии m1 для передачи копии локального объекта как результата работы функции;
— вызов деструктора локального объекта m1 (что логично после выполнения первого пункта).

Деструктор локального объекта был вызван только после вызова конструктора копирования m2/m3.

В итоге — I изменилась не на 10, а на 6.

Ничего компилятор не проигнорировал. В этом случае вообще нет copy elision. Создал временный объект, скопировал его, вызвал деструктор. И так два раза. Итого 6. Деструкторы объектов m2 и m3 вызываются уже при выходе из main(). А вот что действительно вызывает вопрос: почему в вашем выводе не присутствует вывода из этих деструкторов. Корректный результат, который получаю я, должен выглядеть так:

M(100)
M(const M &obj)
~M()

M(-100)
M(const M &obj)
~M()

6
~M()
~M()

В следующем примере у вас вообще undefined behavior, так как вы возвращаете ссылку на временный объект. Обратный порядок вызова копирующего конструктора и деструктора обусловлен тем, что вы не продлеваете время жизни временных объектов, возвращая иx по значению. Они удаляются до выхода из функции.

Copy elision это очень полезная оптимизация, и в 99.999% случаев она обладает не только желаемым, но и вполне очевидным для программиста поведением, а вы её выставляете как какое-то зло и источник проблем.

P.S. Здесь вы забыли вернуть значение:

M &operator=(const M &obj)
    {
        /* Копирующий оператор присваивания */
        i = obj.i;
        cout << "M &operator=(const M &obj)" << endl;
    }
Принципиальную для промышленного использования. Практически весь софт для авионики написан на Ada, C и C++.
У степени двойки установлен всего один бит, у n-1 этот бит будет сброшен и их конъюнкция гарантированно даст 0. Условие выполняется только для степени двойки, т.к. при наличии более одного установленного бита будет сброшен только младший.
Можно сделать и более похоже на Rust:
Сделаем вот такие хелперы:
Код
#include <type_traits>

struct unimplemented {
};

template <class Trait, class For, class Where = void>
struct impl : unimplemented {
};

template <class...>
struct conjunction : std::true_type {
};
template <class B1>
struct conjunction<B1> : B1 {
};
template <class B1, class... Bn>
struct conjunction<B1, Bn...> : std::conditional_t<B1::value != false, conjunction<Bn...>, B1> {
};

template <class T, class Trait>
using implements_single = std::integral_constant<bool, !std::is_base_of<unimplemented, impl<Trait, T>>::value>;

template <class T, class... Traits>
using implements = conjunction<implements_single<T, Traits>...>;

template <class... Ts>
using where = typename std::enable_if<conjunction<Ts...>::value>::type;


Тогда программа будет выглядеть вот так:
template <class U>
struct Container {
    U item;
};

template <class T>
struct Foo {
    template <class U>
    static void implemented( U&& self )
    {
        impl<Foo, typename std::decay<U>::type>::implemented( std::forward<U>( self ) );
    }
};

template <class U>
struct Bar {
};

template <class T, class U>
struct impl<Foo<T>, Container<U>, where<implements<T, Bar<U>>>> {
    static void implemented( const Container<U>& )
    {
    }
};

struct A {
};
struct B {
};

template <>
struct impl<Bar<int>, A> {
};

template <>
struct impl<Bar<float>, B> {
};


int main()
{
    auto c1 = Container<int>{64};
    auto c2 = Container<float>{64.f};
    Foo<A>::implemented( c1 );
    Foo<B>::implemented( c2 );
    // Foo<B>::implemented( c1 );
    // Foo<A>::implemented( c2 );
}
U: Разворот числа

Ни разу не видел более плохого решения, вы хотите получить целое число, но при этом используете логарифмы и экспоненты для такой тривиальной задачи. К тому же у вас по условию только целочисленная арифметика.
public class Solution {
    public static int recursion(int n, int i) {
        return (n==0) ? i : recursion( n/10, i*10 + n%10 );
    }
    public static void main(String[] args) {
        System.out.println(recursion(158, 0));
    }
}
Всё, что «отсутствует» в C++ элементарно может быть реализовано:
#include <boost/variant.hpp>
#include <iostream>
#include <utility>

template <class T, class E>
class Result {
public:
    template <class U>
    Result( U&& obj )
      : value( std::forward<U>( obj ) )
    {
    }

    T unwrap() &&
    {
        assert( value.which() == 0 );
        return std::move( boost::get<T>( value ) );
    }

    template <class F>
    Result or_else( F f ) &&
    {
        if ( value.which() == 0 ) {
            return std::move( boost::get<T>( value ) );
        }
        else {
            return f();
        }
    }

private:
    boost::variant<T, E> value;
};

class File {
public:
    static Result<File, bool> open( const std::string& s )
    {
        if ( s == "exist" ) {
            return File{};
        }
        else {
            return false;
        }
    };
};

int main()
{
    auto file = File::open( "unexist" ).or_else( [] { return File::open( "exist" ); } ).unwrap();
}

Третее – не уверен, что в С++ получится c такой же легкостью передавать в комбинаторы замыкания, которые в результате будут заинлайнены и не будут создавать каких-либо накладных расходов

Всё будет заинлейнено.
Поэтому там, где С++/Java появляются лесницы из try-catch блоков, в Rust удается получить довольно выразительные конструкции

Но что мешает использовать тот же подход в C++/Java?
А еще можно попробовать переписать main «на лету»:
#include <stdint.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

int main()
{
    uintptr_t offset = 0x71;
    size_t pagesize = sysconf( _SC_PAGESIZE );
    char* m = (char*)&main;
    uintptr_t pagestart = ( (uintptr_t)m ) & -pagesize;
    mprotect( (void*)pagestart, ( (uintptr_t)m ) - pagestart + offset, PROT_READ | PROT_WRITE | PROT_EXEC );
    m[offset] = 'e';
    printf( "H%cllo World!\n", 'a' );
}

Information

Rating
Does not participate
Location
Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

Software Architect
PostgreSQL
C#
C++
Linux
Docker
Kubernetes
High-loaded systems
Designing application architecture
Database design